فهرست منبع

Initial video support without audio (#14)

* Adds FFmpeg to CMake
* Refactors to allow support for other wallpaper types
* Updates README.md for compilation requirements
* Initial video support without audio
* Properly support different wallpapers
* Fixes videos not rendering
* Nitpicks
* Moves code related to rendering from Core::CVideo to Render::CVideo
IceCryptonym 6 سال پیش
والد
کامیت
5d5ce361fe

+ 11 - 2
CMakeLists.txt

@@ -15,8 +15,9 @@ find_package(Irrlicht REQUIRED)
 find_package(SDL REQUIRED)
 find_package(SDL_mixer REQUIRED)
 find_package(LZ4 REQUIRED)
+find_package(FFmpeg REQUIRED)
 
-include_directories(${X11_INCLUDE_DIR} ${XRANDR_INCLUDE_DIR} ${IRRLICHT_INCLUDE_DIR} ${LZ4_INCLUDE_DIR} ${SDL_INCLUDE_DIRS} ${SDL_MIXER_INCLUDE_DIRS} src include)
+include_directories(${X11_INCLUDE_DIR} ${XRANDR_INCLUDE_DIR} ${IRRLICHT_INCLUDE_DIR} ${LZ4_INCLUDE_DIR} ${SDL_INCLUDE_DIRS} ${SDL_MIXER_INCLUDE_DIRS} ${FFMPEG_INCLUDE_DIR} src include)
 
 add_executable(
         wallengine
@@ -46,8 +47,12 @@ add_executable(
         src/WallpaperEngine/Render/Shaders/Compiler.h
         src/WallpaperEngine/Render/Shaders/Compiler.cpp
 
+        src/WallpaperEngine/Render/CWallpaper.h
+        src/WallpaperEngine/Render/CWallpaper.cpp
         src/WallpaperEngine/Render/CScene.h
         src/WallpaperEngine/Render/CScene.cpp
+        src/WallpaperEngine/Render/CVideo.h
+        src/WallpaperEngine/Render/CVideo.cpp
         src/WallpaperEngine/Render/CCamera.h
         src/WallpaperEngine/Render/CCamera.cpp
         src/WallpaperEngine/Render/CObject.h
@@ -83,8 +88,12 @@ add_executable(
 
         src/WallpaperEngine/Core/CProject.cpp
         src/WallpaperEngine/Core/CProject.h
+        src/WallpaperEngine/Core/CWallpaper.cpp
+        src/WallpaperEngine/Core/CWallpaper.h
         src/WallpaperEngine/Core/CScene.cpp
         src/WallpaperEngine/Core/CScene.h
+        src/WallpaperEngine/Core/CVideo.cpp
+        src/WallpaperEngine/Core/CVideo.h
         src/WallpaperEngine/Core/CObject.cpp
         src/WallpaperEngine/Core/CObject.h
 
@@ -150,4 +159,4 @@ add_executable(
         src/WallpaperEngine/Core/Objects/Images/Materials/CPass.h
 )
 
-target_link_libraries(wallengine ${X11_LIBRARIES} ${XRANDR_LIBRARIES} ${X11_Xxf86vm_LIB} ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES} ${ZLIB_LIBRARIES} ${IRRLICHT_LIBRARY} ${LZ4_LIBRARY} ${SDL_LIBRARY} ${SDL_MIXER_LIBRARIES})
+target_link_libraries(wallengine ${X11_LIBRARIES} ${XRANDR_LIBRARIES} ${X11_Xxf86vm_LIB} ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES} ${ZLIB_LIBRARIES} ${IRRLICHT_LIBRARY} ${LZ4_LIBRARY} ${SDL_LIBRARY} ${SDL_MIXER_LIBRARIES} ${FFMPEG_LIBRARIES})

+ 110 - 0
CMakeModules/FindFFmpeg.cmake

@@ -0,0 +1,110 @@
+# - Try to find ffmpeg libraries (libavcodec, libavformat, libavutil and libswscale)
+# Once done this will define
+#
+# FFMPEG_FOUND - system has ffmpeg or libav
+# FFMPEG_INCLUDE_DIR - the ffmpeg include directory
+# FFMPEG_LIBRARIES - Link these to use ffmpeg
+# FFMPEG_LIBAVCODEC
+# FFMPEG_LIBAVFORMAT
+# FFMPEG_LIBAVUTIL
+# FFMPEG_LIBSWSCALE
+#
+# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
+# Modified for other libraries by Lasse Kärkkäinen <tronic>
+# Modified for Hedgewars by Stepik777
+# Modified for FFmpeg-example Tuukka Pasanen 2018
+#
+# Redistribution and use is allowed according to the terms of the New
+# BSD license.
+#
+
+include(FindPackageHandleStandardArgs)
+
+    find_package_handle_standard_args(FFMPEG
+        FOUND_VAR FFMPEG_FOUND
+        REQUIRED_VARS
+            FFMPEG_LIBRARY
+            FFMPEG_INCLUDE_DIR
+        VERSION_VAR FFMPEG_VERSION
+    )
+
+if(FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
+  # in cache already
+  set(FFMPEG_FOUND TRUE)
+else()
+  # 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(_FFMPEG_AVCODEC libavcodec)
+    pkg_check_modules(_FFMPEG_AVFORMAT libavformat)
+    pkg_check_modules(_FFMPEG_AVUTIL libavutil)
+    pkg_check_modules(_FFMPEG_SWSCALE libswscale)
+  endif()
+
+  find_path(FFMPEG_AVCODEC_INCLUDE_DIR
+    NAMES libavcodec/avcodec.h
+    PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS}
+      /usr/include
+      /usr/local/include
+      /opt/local/include
+      /sw/include
+    PATH_SUFFIXES ffmpeg libav)
+
+  find_library(FFMPEG_LIBAVCODEC
+    NAMES avcodec
+    PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS}
+      /usr/lib
+      /usr/local/lib
+      /opt/local/lib
+      /sw/lib)
+
+  find_library(FFMPEG_LIBAVFORMAT
+    NAMES avformat
+    PATHS ${_FFMPEG_AVFORMAT_LIBRARY_DIRS}
+      /usr/lib
+      /usr/local/lib
+      /opt/local/lib
+      /sw/lib)
+
+  find_library(FFMPEG_LIBAVUTIL
+    NAMES avutil
+    PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS}
+      /usr/lib
+      /usr/local/lib
+      /opt/local/lib
+      /sw/lib)
+
+   find_library(FFMPEG_LIBSWSCALE
+    NAMES swscale
+    PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS}
+      /usr/lib
+      /usr/local/lib
+      /opt/local/lib
+      /sw/lib)
+
+  if(FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT)
+    set(FFMPEG_FOUND TRUE)
+  endif()
+
+  if(FFMPEG_FOUND)
+    set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
+    set(FFMPEG_LIBRARIES
+      ${FFMPEG_LIBAVCODEC}
+      ${FFMPEG_LIBAVFORMAT}
+      ${FFMPEG_LIBAVUTIL}
+      ${FFMPEG_LIBSWSCALE})
+  endif()
+
+  if(FFMPEG_FOUND)
+    if(NOT FFMPEG_FIND_QUIETLY)
+      message(STATUS
+      "Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
+    endif()
+  else()
+    if(FFMPEG_FIND_REQUIRED)
+      message(FATAL_ERROR
+      "Could not find libavcodec or libavformat or libavutil or libswscale")
+    endif()
+  endif()
+endif()

+ 1 - 0
README.md

@@ -15,6 +15,7 @@ Wallpaper Engine is a software designed by [Kristjan Skutta](https://store.steam
 - ZLIB
 - SDL
 - SDL_mixer
+- FFmpeg
 - X11
 - Xrandr
 

+ 24 - 4
main.cpp

@@ -6,7 +6,9 @@
 
 #include "WallpaperEngine/Core/CProject.h"
 #include "WallpaperEngine/Irrlicht/CContext.h"
+#include "WallpaperEngine/Render/CWallpaper.h"
 #include "WallpaperEngine/Render/CScene.h"
+#include "WallpaperEngine/Render/CVideo.h"
 
 enum BACKGROUND_RUN_MODE
 {
@@ -161,14 +163,32 @@ int main (int argc, char* argv[])
     }
 
     WallpaperEngine::Core::CProject* project = WallpaperEngine::Core::CProject::fromFile (project_path);
-    WallpaperEngine::Render::CScene* scene = new WallpaperEngine::Render::CScene (project, IrrlichtContext);
+    WallpaperEngine::Render::CWallpaper* wallpaper;
 
+    if (project->getType () == "scene")
+    {
+        WallpaperEngine::Core::CScene* scene = project->getWallpaper ()->as <WallpaperEngine::Core::CScene> ();
+        wallpaper = new WallpaperEngine::Render::CScene (scene, IrrlichtContext);
+        IrrlichtContext->getDevice ()->getSceneManager ()->setAmbientLight (
+                    scene->getAmbientColor ().toSColor ()
+        );
+    }
+    else if (project->getType () == "video")
+    {
+        wallpaper = new WallpaperEngine::Render::CVideo (
+                    project->getWallpaper ()->as <WallpaperEngine::Core::CVideo> (),
+                    IrrlichtContext
+        );
+    }
+    else
+    {
+        throw std::runtime_error ("Unsupported wallpaper type");
+    }
+    
     irr::u32 minimumTime = 1000 / maximumFPS;
     irr::u32 startTime = 0;
     irr::u32 endTime = 0;
 
-    IrrlichtContext->getDevice ()->getSceneManager ()->setAmbientLight (scene->getScene ()->getAmbientColor ().toSColor ());
-
     while (IrrlichtContext && IrrlichtContext->getDevice () && IrrlichtContext->getDevice ()->run ())
     {
         if (IrrlichtContext->getDevice ()->getVideoDriver () == nullptr)
@@ -176,7 +196,7 @@ int main (int argc, char* argv[])
 
         startTime = IrrlichtContext->getDevice ()->getTimer ()->getTime ();
 
-        IrrlichtContext->renderFrame (scene);
+        IrrlichtContext->renderFrame (wallpaper);
 
         endTime = IrrlichtContext->getDevice ()->getTimer ()->getTime ();
 

+ 20 - 6
src/WallpaperEngine/Core/CProject.cpp

@@ -1,15 +1,17 @@
 #include <WallpaperEngine/FileSystem/FileSystem.h>
 
 #include "CProject.h"
+#include "CScene.h"
+#include "CVideo.h"
 
 using namespace WallpaperEngine::Core;
 
-CProject::CProject (std::string title, std::string type, CScene *scene) :
+CProject::CProject (std::string title, std::string type, CWallpaper* wallpaper) :
     m_title (std::move (title)),
     m_type (std::move (type)),
-    m_scene (scene)
+    m_wallpaper (wallpaper)
 {
-    this->m_scene->setProject (this);
+    this->m_wallpaper->setProject (this);
 }
 
 CProject* CProject::fromFile (const irr::io::path& filename)
@@ -20,11 +22,23 @@ CProject* CProject::fromFile (const irr::io::path& filename)
     auto type = jsonFindRequired (content, "type", "Project type missing");
     auto file = jsonFindRequired (content, "file", "Project's main file missing");
     auto general = content.find ("general");
+    CWallpaper* wallpaper;
+
+    if (strcmp ((*type).get <std::string> ().c_str (), "scene") == 0)
+    {
+        wallpaper = CScene::fromFile ((*file).get <std::string> ().c_str ());
+    }
+    else if (strcmp ((*type).get <std::string> ().c_str (), "video") == 0)
+    {
+        wallpaper = new CVideo ((*file).get <std::string> ().c_str ());
+    }
+    else
+        throw std::runtime_error ("Unsupported wallpaper type");
 
     CProject* project = new CProject (
         *title,
         *type,
-        CScene::fromFile ((*file).get <std::string> ().c_str ())
+        wallpaper
     );
 
     if (general != content.end ())
@@ -48,9 +62,9 @@ CProject* CProject::fromFile (const irr::io::path& filename)
     return project;
 }
 
-const CScene* CProject::getScene () const
+CWallpaper* CProject::getWallpaper () const
 {
-    return this->m_scene;
+    return this->m_wallpaper;
 }
 
 const std::string& CProject::getTitle () const

+ 5 - 6
src/WallpaperEngine/Core/CProject.h

@@ -2,7 +2,7 @@
 
 #include <irrlicht/irrlicht.h>
 
-#include "CScene.h"
+#include "CWallpaper.h"
 #include "WallpaperEngine/Core/Core.h"
 #include "WallpaperEngine/Core/Projects/CProperty.h"
 
@@ -10,21 +10,21 @@ namespace WallpaperEngine::Core
 {
     using json = nlohmann::json;
 
-    class CScene;
+    class CWallpaper;
 
     class CProject
     {
     public:
         static CProject* fromFile (const irr::io::path& filename);
 
-        const CScene* getScene () const;
+        CWallpaper* getWallpaper () const;
 
         const std::string& getTitle () const;
         const std::string& getType () const;
         const std::vector<Projects::CProperty*>& getProperties () const;
 
     protected:
-        CProject (std::string title, std::string type, CScene* scene);
+        CProject (std::string title, std::string type, CWallpaper* wallpaper);
 
         void insertProperty (Projects::CProperty* property);
     private:
@@ -32,7 +32,6 @@ namespace WallpaperEngine::Core
 
         std::string m_title;
         std::string m_type;
-        CScene* m_scene;
+        CWallpaper* m_wallpaper;
     };
 };
-

+ 3 - 11
src/WallpaperEngine/Core/CScene.cpp

@@ -24,6 +24,7 @@ CScene::CScene (
         irr::video::SColorf clearColor,
         Scenes::CProjection* orthogonalProjection,
         irr::video::SColorf skylightColor) :
+    CWallpaper (Type),
     m_camera (camera),
     m_ambientColor (ambientColor),
     m_bloom (bloom),
@@ -105,7 +106,6 @@ CScene* CScene::fromFile (const irr::io::path& filename)
     return scene;
 }
 
-
 const std::vector<CObject*>& CScene::getObjects () const
 {
     return this->m_objects;
@@ -116,16 +116,6 @@ void CScene::insertObject (CObject* object)
     this->m_objects.push_back (object);
 }
 
-CProject* CScene::getProject ()
-{
-    return this->m_project;
-}
-
-void CScene::setProject (CProject* project)
-{
-    this->m_project = project;
-}
-
 const Scenes::CCamera* CScene::getCamera () const
 {
     return this->m_camera;
@@ -215,3 +205,5 @@ const irr::video::SColorf& CScene::getSkylightColor () const
 {
     return this->m_skylightColor;
 }
+
+const std::string CScene::Type = "scene";

+ 5 - 8
src/WallpaperEngine/Core/CScene.h

@@ -2,8 +2,8 @@
 
 #include <irrlicht/irrlicht.h>
 
-#include "CProject.h"
 #include "CObject.h"
+#include "CWallpaper.h"
 
 #include "Core.h"
 
@@ -14,15 +14,13 @@ namespace WallpaperEngine::Core
 {
     using json = nlohmann::json;
 
-    class CProject;
     class CObject;
 
-    class CScene
+    class CScene : public CWallpaper
     {
     public:
         static CScene* fromFile (const irr::io::path& filename);
 
-        CProject* getProject ();
         const std::vector<CObject*>& getObjects () const;
 
         const irr::video::SColorf& getAmbientColor() const;
@@ -45,9 +43,7 @@ namespace WallpaperEngine::Core
         const Scenes::CCamera* getCamera () const;
 
     protected:
-        friend class CProject;
-
-        void setProject (CProject* project);
+        friend class CWallpaper;
 
         CScene (
                 Scenes::CCamera* camera,
@@ -70,9 +66,10 @@ namespace WallpaperEngine::Core
                 irr::video::SColorf skylightColor
         );
 
+        static const std::string Type;
+
         void insertObject (CObject* object);
     private:
-        CProject* m_project;
         Scenes::CCamera* m_camera;
 
         // data from general section on the json

+ 17 - 0
src/WallpaperEngine/Core/CVideo.cpp

@@ -0,0 +1,17 @@
+#include "CVideo.h"
+
+using namespace WallpaperEngine::Core;
+
+CVideo::CVideo (
+        const irr::io::path& filename) :
+        CWallpaper (Type),
+        m_filename (filename)
+{
+}
+
+const irr::io::path CVideo::getFilename ()
+{
+    return this->m_filename;
+}
+
+const std::string CVideo::Type = "video";

+ 36 - 0
src/WallpaperEngine/Core/CVideo.h

@@ -0,0 +1,36 @@
+#pragma once
+
+#include <irrlicht/irrlicht.h>
+
+#include "Core.h"
+#include "CWallpaper.h"
+
+extern "C"
+{
+    #include <libavcodec/avcodec.h>
+    #include <libavformat/avformat.h>
+    #include <libavutil/imgutils.h>
+    #include <libswscale/swscale.h>
+}
+
+namespace WallpaperEngine::Core
+{
+    class CVideo : public CWallpaper
+    {
+    public:
+        CVideo (
+                const irr::io::path& filename
+        );
+
+        const irr::io::path getFilename ();
+
+    protected:
+        friend class CWallpaper;
+
+        const irr::io::path m_filename;
+
+        static const std::string Type;
+
+    private:
+    };
+};

+ 18 - 0
src/WallpaperEngine/Core/CWallpaper.cpp

@@ -0,0 +1,18 @@
+#include "CWallpaper.h"
+
+using namespace WallpaperEngine::Core;
+
+CWallpaper::CWallpaper (std::string type) :
+    m_type (type)
+{
+}
+
+CProject* CWallpaper::getProject ()
+{
+    return this->m_project;
+}
+
+void CWallpaper::setProject (CProject* project)
+{
+    this->m_project = project;
+}

+ 33 - 0
src/WallpaperEngine/Core/CWallpaper.h

@@ -0,0 +1,33 @@
+#pragma once
+
+#include <assert.h>
+
+#include "CProject.h"
+
+namespace WallpaperEngine::Core
+{
+    class CProject;
+
+    class CWallpaper
+    {
+    public:
+        template<class T> const T* as () const { assert (is <T> ()); return (const T*) this; }
+        template<class T> T* as () { assert (is <T> ()); return (T*) this; }
+
+        template<class T> bool is () { return this->m_type == T::Type; }
+
+        CWallpaper (std::string type);
+
+        CProject* getProject ();
+
+    protected:
+        friend class CProject;
+
+        void setProject (CProject* project);
+
+    private:
+        CProject* m_project;
+
+        std::string m_type;
+    };
+}

+ 6 - 5
src/WallpaperEngine/Irrlicht/CContext.cpp

@@ -162,7 +162,7 @@ void CContext::initializeViewports (irr::SIrrlichtCreationParameters &irrlichtCr
     irrlichtCreationParameters.WindowId = reinterpret_cast<void*> (DefaultRootWindow (display));
 }
 
-void CContext::renderFrame (Render::CScene* scene)
+void CContext::renderFrame (Render::CWallpaper* wallpaper)
 {
     this->m_time = this->getDevice ()->getTimer ()->getTime () / 1000.0f;
     this->m_pointerPosition.X = this->m_eventReceiver->getPosition ().X / (irr::f32) this->getDevice ()->getVideoDriver ()->getScreenSize ().Width;
@@ -170,7 +170,7 @@ void CContext::renderFrame (Render::CScene* scene)
 
     if (this->m_viewports.empty () == true)
     {
-        this->drawScene (scene, true);
+        this->drawWallpaper (wallpaper, true);
     }
     else
     {
@@ -181,14 +181,15 @@ void CContext::renderFrame (Render::CScene* scene)
         {
             // change viewport to render to the correct portion of the display
             this->getDevice ()->getVideoDriver ()->setViewPort (*cur);
-            this->drawScene (scene, false);
+            this->drawWallpaper (wallpaper, false);
         }
     }
 }
 
-void CContext::drawScene (Render::CScene* scene, bool backBuffer)
+void CContext::drawWallpaper (Render::CWallpaper* wallpaper, bool backBuffer)
 {
-    this->getDevice ()->getVideoDriver ()->beginScene (backBuffer, true, scene->getScene ()->getClearColor ().toSColor());
+    // TODO: Get scene clear color
+    this->getDevice ()->getVideoDriver ()->beginScene (backBuffer, true);
     this->getDevice ()->getSceneManager ()->drawAll ();
     this->getDevice ()->getVideoDriver ()->endScene ();
 }

+ 4 - 4
src/WallpaperEngine/Irrlicht/CContext.h

@@ -5,7 +5,7 @@
 
 #include <irrlicht/irrlicht.h>
 
-#include "WallpaperEngine/Render/CScene.h"
+#include "WallpaperEngine/Render/CWallpaper.h"
 
 #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h"
 
@@ -13,7 +13,7 @@
 
 namespace WallpaperEngine::Render
 {
-    class CScene;
+    class CWallpaper;
 };
 
 namespace WallpaperEngine::Irrlicht
@@ -29,7 +29,7 @@ namespace WallpaperEngine::Irrlicht
         void insertShaderVariable (Render::Shaders::Variables::CShaderVariable* variable);
         const std::vector<Render::Shaders::Variables::CShaderVariable*>& getShaderVariables () const;
 
-        void renderFrame (Render::CScene* scene);
+        void renderFrame (Render::CWallpaper* wallpaper);
 
         irr::IrrlichtDevice* getDevice ();
         irr::io::path resolveMaterials (const std::string& materialName);
@@ -38,7 +38,7 @@ namespace WallpaperEngine::Irrlicht
         irr::io::path resolveIncludeShader (const std::string& includeShader);
     private:
         void initializeViewports (irr::SIrrlichtCreationParameters& irrlichtCreationParameters);
-        void drawScene (Render::CScene* scene, bool backBuffer);
+        void drawWallpaper (Render::CWallpaper* wallpaper, bool backBuffer);
 
         irr::io::path resolveFile (const irr::io::path& file);
 

+ 10 - 37
src/WallpaperEngine/Render/CScene.cpp

@@ -1,5 +1,3 @@
-#include "WallpaperEngine/Irrlicht/CContext.h"
-
 #include "WallpaperEngine/Core/Objects/CImage.h"
 #include "WallpaperEngine/Core/Objects/CSound.h"
 
@@ -11,23 +9,17 @@
 using namespace WallpaperEngine;
 using namespace WallpaperEngine::Render;
 
-CScene::CScene (const Core::CProject* project, Irrlicht::CContext* context) :
-    irr::scene::ISceneNode (
-        context->getDevice ()->getSceneManager ()->getRootSceneNode (),
-        context->getDevice ()->getSceneManager ()
-    ),
-    m_project (project),
-    m_scene (project->getScene ()),
-    m_context (context)
+CScene::CScene (Core::CScene* scene, Irrlicht::CContext* context) :
+    CWallpaper (scene, Type, context)
 {
-    this->m_camera = new CCamera (this, this->m_project->getScene ()->getCamera ());
+    this->m_camera = new CCamera (this, scene->getCamera ());
     this->m_camera->setOrthogonalProjection (
-            this->m_scene->getOrthogonalProjection ()->getWidth (),
-            this->m_scene->getOrthogonalProjection ()->getHeight ()
+            scene->getOrthogonalProjection ()->getWidth (),
+            scene->getOrthogonalProjection ()->getHeight ()
     );
 
-    auto cur = this->m_scene->getObjects ().begin ();
-    auto end = this->m_scene->getObjects ().end ();
+    auto cur = scene->getObjects ().begin ();
+    auto end = scene->getObjects ().end ();
 
     int highestId = 0;
 
@@ -48,21 +40,6 @@ CScene::CScene (const Core::CProject* project, Irrlicht::CContext* context) :
 
     this->m_nextId = ++highestId;
     this->setAutomaticCulling (irr::scene::EAC_OFF);
-    this->m_boundingBox = irr::core::aabbox3d<irr::f32>(0, 0, 0, 0, 0, 0);
-}
-
-CScene::~CScene ()
-{
-}
-
-Irrlicht::CContext* CScene::getContext ()
-{
-    return this->m_context;
-}
-
-const Core::CScene* CScene::getScene () const
-{
-    return this->m_scene;
 }
 
 CCamera* CScene::getCamera () const
@@ -79,13 +56,9 @@ void CScene::render ()
 {
 }
 
-const irr::core::aabbox3d<irr::f32>& CScene::getBoundingBox () const
+Core::CScene* CScene::getScene ()
 {
-    return this->m_boundingBox;
+    return this->getWallpaperData ()->as<Core::CScene> ();
 }
-void CScene::OnRegisterSceneNode ()
-{
-    SceneManager->registerNodeForRendering (this);
 
-    ISceneNode::OnRegisterSceneNode ();
-}
+const std::string CScene::Type = "scene";

+ 12 - 17
src/WallpaperEngine/Render/CScene.h

@@ -2,40 +2,35 @@
 
 #include "CCamera.h"
 
-#include "WallpaperEngine/Core/CProject.h"
 #include "WallpaperEngine/Core/CScene.h"
 
-#include "WallpaperEngine/Irrlicht/CContext.h"
+#include "WallpaperEngine/Render/CWallpaper.h"
 
-namespace WallpaperEngine::Irrlicht
-{
-    class CContext;
-};
+#include "WallpaperEngine/Irrlicht/CContext.h"
 
 namespace WallpaperEngine::Render
 {
     class CCamera;
 
-    class CScene : public irr::scene::ISceneNode
+    class CScene : public CWallpaper
     {
     public:
-        CScene (const Core::CProject* project, Irrlicht::CContext* context);
-        ~CScene () override;
+        CScene (Core::CScene* scene, WallpaperEngine::Irrlicht::CContext* context);
 
-        Irrlicht::CContext* getContext ();
-        const Core::CScene* getScene () const;
         CCamera* getCamera () const;
         int nextId ();
 
         void render () override;
-        const irr::core::aabbox3d<irr::f32>& getBoundingBox() const override;
-        void OnRegisterSceneNode () override;
+
+        Core::CScene* getScene ();
+
+    protected:
+        friend class CWallpaper;
+
+        static const std::string Type;
+
     private:
-        const Core::CProject* m_project;
-        const Core::CScene* m_scene;
         CCamera* m_camera;
-        Irrlicht::CContext* m_context;
         irr::u32 m_nextId;
-        irr::core::aabbox3d<irr::f32> m_boundingBox;
     };
 }

+ 165 - 0
src/WallpaperEngine/Render/CVideo.cpp

@@ -0,0 +1,165 @@
+#include "CVideo.h"
+
+using namespace WallpaperEngine;
+
+using namespace WallpaperEngine::Render;
+
+CVideo::CVideo (Core::CVideo* video, WallpaperEngine::Irrlicht::CContext* context) :
+    CWallpaper (video, Type, context)
+{
+    if (avformat_open_input (&m_formatCtx, video->getFilename ().c_str (), NULL, NULL) < 0)
+        throw std::runtime_error ("Failed to open video file");
+
+    if (avformat_find_stream_info (m_formatCtx, NULL) < 0)
+        throw std::runtime_error ("Failed to get stream info");
+
+    // Find first video stream
+    for (int i = 0; i < m_formatCtx->nb_streams; i++)
+    {
+        if (m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+        {
+            m_videoStream = i;
+            break;
+        }
+    }
+
+    // Find first audio stream
+    for (int i = 0; i < m_formatCtx->nb_streams; i++)
+    {
+        if (m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
+        {
+            m_audioStream = i; 
+            break;
+        }
+    }
+
+    // Only video stream is required
+    if (m_videoStream == -1)
+        throw std::runtime_error ("Failed to find video stream");
+
+    AVCodec* codec = avcodec_find_decoder (m_formatCtx->streams[m_videoStream]->codecpar->codec_id);
+    if (codec == nullptr)
+        throw std::runtime_error ("Failed to find codec");
+
+    m_codecCtx = avcodec_alloc_context3 (codec);
+    if (avcodec_parameters_to_context (m_codecCtx, m_formatCtx->streams[m_videoStream]->codecpar))
+        throw std::runtime_error ("Failed to copy codec parameters");
+
+    if (avcodec_open2 (m_codecCtx, codec, NULL) < 0)
+        throw std::runtime_error ("Failed to open codec");
+
+    m_videoFrame = av_frame_alloc ();
+    m_videoFrameRGB = av_frame_alloc ();
+    if (m_videoFrameRGB == nullptr)
+        throw std::runtime_error ("Failed to allocate video frame");
+}
+
+void CVideo::setSize (int width, int height)
+{
+    if (m_buffer != nullptr)
+        av_free (m_buffer);
+
+    if (m_swsCtx != nullptr)
+        sws_freeContext (m_swsCtx);
+
+    int numBytes = av_image_get_buffer_size (AV_PIX_FMT_RGB24, width, height, 1);
+    m_buffer = (uint8_t*)av_malloc (numBytes * sizeof (uint8_t));
+
+    av_image_fill_arrays (m_videoFrameRGB->data, m_videoFrameRGB->linesize, m_buffer, AV_PIX_FMT_RGB24, width, height, 1);
+
+    m_swsCtx = sws_getContext (m_codecCtx->width, m_codecCtx->height,
+                    m_codecCtx->pix_fmt,
+                    width, height,
+                    AV_PIX_FMT_RGB24,
+                    SWS_BILINEAR, NULL, NULL, NULL);
+}
+
+void CVideo::render ()
+{
+    irr::video::IVideoDriver* driver = m_context->getDevice ()->getVideoDriver ();
+    int width = driver->getScreenSize ().Width;
+    int height = driver->getScreenSize ().Height;
+
+    m_frameImage = m_context->getDevice ()->getVideoDriver ()->createImage (irr::video::ECOLOR_FORMAT::ECF_R8G8B8,
+                irr::core::dimension2du(width, height));
+
+    setSize (width, height);
+    getNextFrame ();
+    writeFrameToImage ();
+
+    driver->removeTexture (m_frameTexture);
+    m_frameTexture = driver->addTexture ("frameTexture", m_frameImage);
+    m_frameImage->drop ();
+
+    driver->draw2DImage (m_frameTexture, irr::core::vector2di(0));
+}
+
+void CVideo::getNextFrame ()
+{
+    bool eof = false;
+    AVPacket packet;
+    packet.data = nullptr;
+    
+    // Find video streams packet
+    do
+    {
+        if (packet.data != nullptr)
+            av_packet_unref (&packet);
+
+        int readError = av_read_frame (m_formatCtx, &packet);
+        if (readError == AVERROR_EOF)
+        {
+            eof = true;
+            break;
+        }
+        else if (readError < 0)
+        {
+            char err[AV_ERROR_MAX_STRING_SIZE];
+            throw std::runtime_error (av_make_error_string (err, AV_ERROR_MAX_STRING_SIZE, readError));
+        }
+        
+    } while (packet.stream_index != m_videoStream);
+
+    // Send video stream packet to codec
+    if (avcodec_send_packet (m_codecCtx, &packet) < 0)
+        return;
+    
+    // Receive frame from codec
+    if (avcodec_receive_frame (m_codecCtx, m_videoFrame) < 0)
+        return;
+
+    sws_scale (m_swsCtx, (uint8_t const* const*)m_videoFrame->data, m_videoFrame->linesize,
+            0, m_codecCtx->height, m_videoFrameRGB->data, m_videoFrameRGB->linesize);
+
+    av_packet_unref (&packet);
+
+    if (eof)
+        restartStream ();
+}
+
+void CVideo::writeFrameToImage ()
+{
+    uint8_t* frameData = m_videoFrameRGB->data[0];
+    if (frameData == nullptr)
+        return;
+
+    irr::u32 imgWidth = m_frameImage->getDimension().Width;
+    irr::u32 imgHeight = m_frameImage->getDimension().Height;
+    
+    unsigned char* data = (unsigned char*)m_frameImage->lock ();
+    memcpy (data, frameData, imgWidth * imgHeight * 3);
+    m_frameImage->unlock ();
+}
+
+void CVideo::restartStream ()
+{
+    av_seek_frame (m_formatCtx, m_videoStream, 0, AVSEEK_FLAG_FRAME);
+    avcodec_flush_buffers (m_codecCtx);
+}
+
+Core::CVideo* CVideo::getVideo ()
+{
+    return this->getWallpaperData ()->as<Core::CVideo> ();
+}
+
+const std::string CVideo::Type = "video";

+ 52 - 0
src/WallpaperEngine/Render/CVideo.h

@@ -0,0 +1,52 @@
+#pragma once
+
+#include <irrlicht/irrlicht.h>
+
+#include "WallpaperEngine/Core/CVideo.h"
+
+#include "WallpaperEngine/Render/CWallpaper.h"
+
+#include "WallpaperEngine/Irrlicht/CContext.h"
+
+extern "C"
+{
+    #include <libavcodec/avcodec.h>
+    #include <libavformat/avformat.h>
+    #include <libavutil/imgutils.h>
+    #include <libswscale/swscale.h>
+}
+
+namespace WallpaperEngine::Render
+{
+    class CVideo : public CWallpaper
+    {
+    public:
+        CVideo (Core::CVideo* video, WallpaperEngine::Irrlicht::CContext* context);
+
+        void render () override;
+
+        Core::CVideo* getVideo ();
+
+    protected:
+        friend class CWallpaper;
+
+        static const std::string Type;
+
+    private:
+        void setSize (int width, int height);
+        void restartStream ();
+        void getNextFrame ();
+        void writeFrameToImage ();
+
+        AVFormatContext* m_formatCtx = nullptr;
+        AVCodecContext* m_codecCtx = nullptr;
+        AVFrame* m_videoFrame = nullptr;
+        AVFrame* m_videoFrameRGB = nullptr;
+        SwsContext* m_swsCtx = nullptr;
+        uint8_t* m_buffer = nullptr;
+        int m_videoStream = -1, m_audioStream = -1;
+
+        irr::video::IImage* m_frameImage;
+        irr::video::ITexture* m_frameTexture;
+    };
+};

+ 40 - 0
src/WallpaperEngine/Render/CWallpaper.cpp

@@ -0,0 +1,40 @@
+#include "CWallpaper.h"
+
+using namespace WallpaperEngine::Render;
+
+CWallpaper::CWallpaper (Core::CWallpaper* wallpaperData, std::string type, WallpaperEngine::Irrlicht::CContext* context) :
+    irr::scene::ISceneNode (
+        context->getDevice ()->getSceneManager ()->getRootSceneNode (),
+        context->getDevice ()->getSceneManager ()
+    ),
+    m_context (context),
+    m_wallpaperData (wallpaperData),
+    m_type (type)
+{
+}
+
+CWallpaper::~CWallpaper ()
+{
+}
+
+void CWallpaper::OnRegisterSceneNode ()
+{
+    SceneManager->registerNodeForRendering (this);
+
+    ISceneNode::OnRegisterSceneNode ();
+}
+
+WallpaperEngine::Irrlicht::CContext* CWallpaper::getContext () const
+{
+    return this->m_context;
+}
+
+const irr::core::aabbox3d<irr::f32>& CWallpaper::getBoundingBox () const
+{
+    return this->m_boundingBox;
+}
+
+WallpaperEngine::Core::CWallpaper* CWallpaper::getWallpaperData ()
+{
+    return this->m_wallpaperData;
+}

+ 45 - 0
src/WallpaperEngine/Render/CWallpaper.h

@@ -0,0 +1,45 @@
+#pragma once
+
+#include <irrlicht/irrlicht.h>
+
+#include "WallpaperEngine/Core/CWallpaper.h"
+#include "WallpaperEngine/Core/CScene.h"
+#include "WallpaperEngine/Core/CVideo.h"
+
+#include "WallpaperEngine/Irrlicht/CContext.h"
+
+namespace WallpaperEngine::Irrlicht
+{
+    class CContext;
+};
+
+namespace WallpaperEngine::Render
+{
+    class CWallpaper : public irr::scene::ISceneNode
+    {
+    public:
+        template<class T> const T* as () const { assert (is<T> ()); return (const T*) this; }
+        template<class T> T* as () { assert (is<T> ()); return (T*) this; }
+
+        template<class T> bool is () { return this->m_type == T::Type; }
+
+        CWallpaper (Core::CWallpaper* wallpaperData, std::string type, WallpaperEngine::Irrlicht::CContext* context);
+        ~CWallpaper () override;
+
+        void OnRegisterSceneNode () override;
+
+        WallpaperEngine::Irrlicht::CContext* getContext () const;
+        const irr::core::aabbox3d<irr::f32>& getBoundingBox () const override;
+
+    protected:
+        WallpaperEngine::Irrlicht::CContext* m_context;
+        Core::CWallpaper* m_wallpaperData;
+
+        Core::CWallpaper* getWallpaperData ();
+
+    private:
+        irr::core::aabbox3d<irr::f32> m_boundingBox = irr::core::aabbox3d<irr::f32> (0, 0, 0, 0, 0, 0);
+
+        std::string m_type;
+    };
+}