mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Add initial video playback support for Ogg Theora videos (resolves issue #66.)
The basic APIs are:
video = love.graphics.newVideo("myvideo.ogv")
love.graphics.draw(video, ...) -- Video objects are Drawables.
video:play(), video:pause()
video:getDuration(), video:tell(), video:rewind(), video:seek(seconds)
video:getSource()
video:getWidth(), video:getHeight(), video:setFilter(min, mag)
More advanced APIs include video:setSource(source), video:getStream(), and videostream:setSync.
To use a custom pixel shader when drawing a Video, call the new TexelVideo(texcoords) function instead of Texel(texture, texcoords) in order to get the pixel colors of a video frame.
This commit is contained in:
@@ -308,6 +308,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
|
||||
src/modules/graphics/opengl/SpriteBatch.h
|
||||
src/modules/graphics/opengl/Text.cpp
|
||||
src/modules/graphics/opengl/Text.h
|
||||
src/modules/graphics/opengl/Video.cpp
|
||||
src/modules/graphics/opengl/Video.h
|
||||
src/modules/graphics/opengl/wrap_Canvas.cpp
|
||||
src/modules/graphics/opengl/wrap_Canvas.h
|
||||
src/modules/graphics/opengl/wrap_Font.cpp
|
||||
@@ -326,6 +328,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
|
||||
src/modules/graphics/opengl/wrap_SpriteBatch.h
|
||||
src/modules/graphics/opengl/wrap_Text.cpp
|
||||
src/modules/graphics/opengl/wrap_Text.h
|
||||
src/modules/graphics/opengl/wrap_Video.cpp
|
||||
src/modules/graphics/opengl/wrap_Video.h
|
||||
)
|
||||
|
||||
set(LOVE_SRC_MODULE_GRAPHICS
|
||||
@@ -778,6 +782,35 @@ set(LOVE_SRC_MODULE_TOUCH
|
||||
source_group("modules\\touch" FILES ${LOVE_SRC_MODULE_TOUCH_ROOT})
|
||||
source_group("modules\\touch\\sdl" FILES ${LOVE_SRC_MODULE_TOUCH_SDL})
|
||||
|
||||
#
|
||||
# love.video
|
||||
#
|
||||
|
||||
set(LOVE_SRC_MODULE_VIDEO_ROOT
|
||||
src/modules/video/Video.h
|
||||
src/modules/video/VideoStream.cpp
|
||||
src/modules/video/VideoStream.h
|
||||
src/modules/video/wrap_Video.cpp
|
||||
src/modules/video/wrap_Video.h
|
||||
src/modules/video/wrap_VideoStream.cpp
|
||||
src/modules/video/wrap_VideoStream.h
|
||||
)
|
||||
|
||||
set(LOVE_SRC_MODULE_VIDEO_THEORA
|
||||
src/modules/video/theora/Video.cpp
|
||||
src/modules/video/theora/Video.h
|
||||
src/modules/video/theora/VideoStream.cpp
|
||||
src/modules/video/theora/VideoStream.h
|
||||
)
|
||||
|
||||
set(LOVE_SRC_MODULE_VIDEO
|
||||
${LOVE_SRC_MODULE_VIDEO_ROOT}
|
||||
${LOVE_SRC_MODULE_VIDEO_THEORA}
|
||||
)
|
||||
|
||||
source_group("modules\\video" FILES ${LOVE_SRC_MODULE_VIDEO_ROOT})
|
||||
source_group("modules\\video\\theora" FILES ${LOVE_SRC_MODULE_VIDEO_THEORA})
|
||||
|
||||
#
|
||||
# love.window
|
||||
#
|
||||
@@ -1203,6 +1236,7 @@ set(LOVE_LIB_SRC
|
||||
${LOVE_SRC_MODULE_THREAD}
|
||||
${LOVE_SRC_MODULE_TIMER}
|
||||
${LOVE_SRC_MODULE_TOUCH}
|
||||
${LOVE_SRC_MODULE_VIDEO}
|
||||
${LOVE_SRC_MODULE_WINDOW}
|
||||
)
|
||||
|
||||
@@ -1222,6 +1256,7 @@ set(LOVE_MEGA_3P
|
||||
${MEGA_LIBOGG}
|
||||
${MEGA_LIBVORBISFILE}
|
||||
${MEGA_LIBVORBIS}
|
||||
${MEGA_LIBTHEORA}
|
||||
${MEGA_LUA}
|
||||
${MEGA_MODPLUG}
|
||||
${MEGA_OPENAL}
|
||||
|
||||
@@ -53,6 +53,7 @@ PKG_CHECK_MODULES([openal], [openal], [], [LOVE_MSG_ERROR([OpenAL])])
|
||||
PKG_CHECK_MODULES([libmodplug], [libmodplug], [], [LOVE_MSG_ERROR([libmodplug])])
|
||||
PKG_CHECK_MODULES([vorbisfile], [vorbisfile], [], [LOVE_MSG_ERROR([libvorbis and libvorbisfile])])
|
||||
PKG_CHECK_MODULES([zlib], [zlib], [], [LOVE_MSG_ERROR([zlib])])
|
||||
PKG_CHECK_MODULES([theora], [theoradec], [], [LOVE_MSG_ERROR([libtheora])])
|
||||
|
||||
# Other libraries
|
||||
AC_SEARCH_LIBS([sqrt], [m], [], [LOVE_MSG_ERROR([the C math library])])
|
||||
|
||||
@@ -114,7 +114,7 @@ cat > src/Makefile.am << EOF
|
||||
AM_CPPFLAGS = -I$inc_current -I$inc_modules -I$inc_libraries -I$inc_libraries/enet/libenet/include \$(LOVE_INCLUDES) \$(FILE_OFFSET)\
|
||||
\$(SDL_CFLAGS) \$(lua_CFLAGS) \$(freetype2_CFLAGS)\
|
||||
\$(openal_CFLAGS) \$(zlib_CFLAGS) \$(libmodplug_CFLAGS)\
|
||||
\$(vorbisfile_CFLAGS)
|
||||
\$(vorbisfile_CFLAGS) \$(theora_CFLAGS)
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
SUBDIRS =
|
||||
SUFFIXES = .lua .lua.h
|
||||
@@ -146,7 +146,7 @@ liblove${love_amsuffix}_la_LDFLAGS = -module -export-dynamic \$(LDFLAGS)
|
||||
liblove${love_amsuffix}_la_LIBADD = \
|
||||
\$(SDL_LIBS) \$(freetype2_LIBS) \$(lua_LIBS)\
|
||||
\$(openal_LIBS) \$(zlib_LIBS) \$(libmodplug_LIBS)\
|
||||
\$(vorbisfile_LIBS)
|
||||
\$(vorbisfile_LIBS) \$(theora_LIBS)
|
||||
EOF
|
||||
|
||||
genmodules >> src/Makefile.am
|
||||
|
||||
@@ -53,6 +53,7 @@ public:
|
||||
M_TIMER,
|
||||
M_TOUCH,
|
||||
M_WINDOW,
|
||||
M_VIDEO,
|
||||
M_MAX_ENUM
|
||||
};
|
||||
|
||||
|
||||
@@ -117,6 +117,16 @@ public:
|
||||
return object;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return object != nullptr;
|
||||
}
|
||||
|
||||
operator T*() const
|
||||
{
|
||||
return object;
|
||||
}
|
||||
|
||||
void set(T *obj)
|
||||
{
|
||||
if (obj) obj->retain();
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_STREAM_H
|
||||
#define LOVE_STREAM_H
|
||||
|
||||
// LOVE
|
||||
#include <stddef.h>
|
||||
#include "Object.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
|
||||
class Stream : public Object
|
||||
{
|
||||
public:
|
||||
virtual ~Stream() {}
|
||||
|
||||
// getData and getSize are assumed to talk about
|
||||
// the buffer
|
||||
|
||||
/**
|
||||
* A callback, gets called when some Stream consumer exhausts the data
|
||||
**/
|
||||
virtual void fillBackBuffer() {}
|
||||
|
||||
/**
|
||||
* Get the front buffer, Streams are supposed to be (at least) double-buffered
|
||||
**/
|
||||
virtual const void *getFrontBuffer() const = 0;
|
||||
|
||||
/**
|
||||
* Get the size of any (and in particular the front) buffer
|
||||
**/
|
||||
virtual size_t getSize() const = 0;
|
||||
|
||||
/**
|
||||
* Swap buffers. Returns true if there is new data in the front buffer,
|
||||
* false otherwise.
|
||||
* NOTE: If there is no back buffer ready, this call must be ignored
|
||||
**/
|
||||
virtual bool swapBuffers() = 0;
|
||||
}; // Stream
|
||||
|
||||
} // love
|
||||
|
||||
#endif // LOVE_STREAM_H
|
||||
@@ -141,6 +141,8 @@
|
||||
# define LOVE_ENABLE_TOUCH
|
||||
# define LOVE_ENABLE_TOUCH_SDL
|
||||
# define LOVE_ENABLE_UTF8
|
||||
# define LOVE_ENABLE_VIDEO
|
||||
# define LOVE_ENABLE_VIDEO_THEORA
|
||||
# define LOVE_ENABLE_WINDOW
|
||||
# define LOVE_ENABLE_WINDOW_SDL
|
||||
# define LOVE_ENABLE_WUFF
|
||||
|
||||
@@ -34,6 +34,7 @@ static const TypeBits *createTypeFlags()
|
||||
b[OBJECT_ID] = one << OBJECT_ID;
|
||||
b[DATA_ID] = (one << DATA_ID) | b[OBJECT_ID];
|
||||
b[MODULE_ID] = (one << MODULE_ID) | b[OBJECT_ID];
|
||||
b[STREAM_ID] = (one << STREAM_ID) | b[OBJECT_ID];
|
||||
|
||||
// Filesystem.
|
||||
b[FILESYSTEM_FILE_ID] = (one << FILESYSTEM_FILE_ID) | b[OBJECT_ID];
|
||||
@@ -55,6 +56,7 @@ static const TypeBits *createTypeFlags()
|
||||
b[GRAPHICS_SHADER_ID] = (one << GRAPHICS_SHADER_ID) | b[OBJECT_ID];
|
||||
b[GRAPHICS_MESH_ID] = (one << GRAPHICS_MESH_ID) | b[GRAPHICS_DRAWABLE_ID];
|
||||
b[GRAPHICS_TEXT_ID] = (one << GRAPHICS_TEXT_ID) | b[GRAPHICS_DRAWABLE_ID];
|
||||
b[GRAPHICS_VIDEO_ID] = (one << GRAPHICS_VIDEO_ID) | b[GRAPHICS_DRAWABLE_ID];
|
||||
|
||||
// Image.
|
||||
b[IMAGE_IMAGE_DATA_ID] = (one << IMAGE_IMAGE_DATA_ID) | b[DATA_ID];
|
||||
@@ -105,6 +107,9 @@ static const TypeBits *createTypeFlags()
|
||||
b[THREAD_THREAD_ID] = (one << THREAD_THREAD_ID) | b[OBJECT_ID];
|
||||
b[THREAD_CHANNEL_ID] = (one << THREAD_CHANNEL_ID) | b[OBJECT_ID];
|
||||
|
||||
// Video
|
||||
b[VIDEO_VIDEO_STREAM_ID] = (one << VIDEO_VIDEO_STREAM_ID) | b[STREAM_ID];
|
||||
|
||||
// Modules.
|
||||
b[MODULE_FILESYSTEM_ID] = (one << MODULE_FILESYSTEM_ID) | b[MODULE_ID];
|
||||
b[MODULE_GRAPHICS_ID] = (one << MODULE_GRAPHICS_ID) | b[MODULE_ID];
|
||||
|
||||
@@ -34,6 +34,7 @@ enum Type
|
||||
OBJECT_ID,
|
||||
DATA_ID,
|
||||
MODULE_ID,
|
||||
STREAM_ID,
|
||||
|
||||
// Filesystem.
|
||||
FILESYSTEM_FILE_ID,
|
||||
@@ -56,6 +57,7 @@ enum Type
|
||||
GRAPHICS_SHADER_ID,
|
||||
GRAPHICS_MESH_ID,
|
||||
GRAPHICS_TEXT_ID,
|
||||
GRAPHICS_VIDEO_ID,
|
||||
|
||||
// Image
|
||||
IMAGE_IMAGE_DATA_ID,
|
||||
@@ -106,6 +108,9 @@ enum Type
|
||||
THREAD_THREAD_ID,
|
||||
THREAD_CHANNEL_ID,
|
||||
|
||||
// Video
|
||||
VIDEO_VIDEO_STREAM_ID,
|
||||
|
||||
// The modules themselves. Only add abstracted modules here.
|
||||
MODULE_FILESYSTEM_ID,
|
||||
MODULE_GRAPHICS_ID,
|
||||
|
||||
@@ -155,19 +155,28 @@ int w_newFile(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
FileData *luax_getfiledata(lua_State *L, int idx)
|
||||
File *luax_getfile(lua_State *L, int idx)
|
||||
{
|
||||
FileData *data = nullptr;
|
||||
File *file = nullptr;
|
||||
|
||||
if (lua_isstring(L, idx))
|
||||
{
|
||||
const char *filename = luaL_checkstring(L, idx);
|
||||
file = instance()->newFile(filename);
|
||||
}
|
||||
else if (luax_istype(L, idx, FILESYSTEM_FILE_ID))
|
||||
{
|
||||
else
|
||||
file = luax_checkfile(L, idx);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
FileData *luax_getfiledata(lua_State *L, int idx)
|
||||
{
|
||||
FileData *data = nullptr;
|
||||
File *file = nullptr;
|
||||
|
||||
if (lua_isstring(L, idx) || luax_istype(L, idx, FILESYSTEM_FILE_ID))
|
||||
{
|
||||
file = luax_getfile(L, idx);
|
||||
file->retain();
|
||||
}
|
||||
else if (luax_istype(L, idx, FILESYSTEM_FILE_DATA_ID))
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/runtime.h"
|
||||
#include "File.h"
|
||||
#include "FileData.h"
|
||||
|
||||
namespace love
|
||||
@@ -38,6 +39,7 @@ namespace filesystem
|
||||
* May trigger a Lua error.
|
||||
**/
|
||||
FileData *luax_getfiledata(lua_State *L, int idx);
|
||||
File *luax_getfile(lua_State *L, int idx);
|
||||
|
||||
bool hack_setupWriteDirectory();
|
||||
int loader(lua_State *L);
|
||||
|
||||
@@ -86,6 +86,11 @@ Graphics::~Graphics()
|
||||
Shader::defaultShader->release();
|
||||
Shader::defaultShader = nullptr;
|
||||
}
|
||||
if (Shader::defaultVideoShader)
|
||||
{
|
||||
Shader::defaultVideoShader->release();
|
||||
Shader::defaultVideoShader = nullptr;
|
||||
}
|
||||
|
||||
if (quadIndices)
|
||||
delete quadIndices;
|
||||
@@ -320,6 +325,13 @@ bool Graphics::setMode(int width, int height)
|
||||
Shader::defaultShader = newShader(Shader::defaultCode[renderer]);
|
||||
}
|
||||
|
||||
// and a default video shader.
|
||||
if (!Shader::defaultVideoShader)
|
||||
{
|
||||
Renderer renderer = GLAD_ES_VERSION_2_0 ? RENDERER_OPENGLES : RENDERER_OPENGL;
|
||||
Shader::defaultVideoShader = newShader(Shader::defaultVideoCode[renderer]);
|
||||
}
|
||||
|
||||
// A shader should always be active, but the default shader shouldn't be
|
||||
// returned by getShader(), so we don't do setShader(defaultShader).
|
||||
if (!Shader::current)
|
||||
@@ -819,6 +831,11 @@ Text *Graphics::newText(Font *font, const std::vector<Font::ColoredString> &text
|
||||
return new Text(font, text);
|
||||
}
|
||||
|
||||
Video *Graphics::newVideo(love::video::VideoStream *stream)
|
||||
{
|
||||
return new Video(stream);
|
||||
}
|
||||
|
||||
bool Graphics::isGammaCorrect() const
|
||||
{
|
||||
return love::graphics::isGammaCorrect();
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
#include "window/Window.h"
|
||||
|
||||
#include "video/VideoStream.h"
|
||||
|
||||
#include "Font.h"
|
||||
#include "Image.h"
|
||||
#include "graphics/Quad.h"
|
||||
@@ -47,6 +49,7 @@
|
||||
#include "Shader.h"
|
||||
#include "Mesh.h"
|
||||
#include "Text.h"
|
||||
#include "Video.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -185,6 +188,8 @@ public:
|
||||
|
||||
Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
|
||||
|
||||
Video *newVideo(love::video::VideoStream *stream);
|
||||
|
||||
bool isGammaCorrect() const;
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,8 +64,10 @@ namespace
|
||||
|
||||
Shader *Shader::current = nullptr;
|
||||
Shader *Shader::defaultShader = nullptr;
|
||||
Shader *Shader::defaultVideoShader = nullptr;
|
||||
|
||||
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||
Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM];
|
||||
|
||||
std::vector<int> Shader::textureCounters;
|
||||
|
||||
@@ -77,6 +79,7 @@ Shader::Shader(const ShaderSource &source)
|
||||
, lastCanvas((Canvas *) -1)
|
||||
, lastViewport()
|
||||
, lastPointSize(0.0f)
|
||||
, videoTextureUnits()
|
||||
{
|
||||
if (source.vertex.empty() && source.pixel.empty())
|
||||
throw love::Exception("Cannot create shader: no source code!");
|
||||
@@ -225,6 +228,9 @@ bool Shader::loadVolatile()
|
||||
lastProjectionMatrix.setTranslation(nan, nan);
|
||||
lastTransformMatrix.setTranslation(nan, nan);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
videoTextureUnits[i] = 0;
|
||||
|
||||
// zero out active texture list
|
||||
activeTexUnits.clear();
|
||||
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
|
||||
@@ -635,6 +641,57 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const
|
||||
return builtinAttributes[int(attrib)] != -1;
|
||||
}
|
||||
|
||||
void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
|
||||
// Set up the texture units that will be used by the shader to sample from
|
||||
// the textures, if they haven't been set up yet.
|
||||
if (videoTextureUnits[0] == 0)
|
||||
{
|
||||
const GLint locs[3] = {
|
||||
builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL],
|
||||
builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL],
|
||||
builtinUniforms[BUILTIN_VIDEO_CR_CHANNEL]
|
||||
};
|
||||
|
||||
const char *names[3] = {nullptr, nullptr, nullptr};
|
||||
builtinNames.find(BUILTIN_VIDEO_Y_CHANNEL, names[0]);
|
||||
builtinNames.find(BUILTIN_VIDEO_CB_CHANNEL, names[1]);
|
||||
builtinNames.find(BUILTIN_VIDEO_CR_CHANNEL, names[2]);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (locs[i] >= 0 && names[i] != nullptr)
|
||||
{
|
||||
videoTextureUnits[i] = getTextureUnit(names[i]);
|
||||
|
||||
// Increment global shader texture id counter for this texture
|
||||
// unit, if we haven't already.
|
||||
if (activeTexUnits[videoTextureUnits[i] - 1] == 0)
|
||||
++textureCounters[videoTextureUnits[i] - 1];
|
||||
|
||||
glUniform1i(locs[i], videoTextureUnits[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const GLuint textures[3] = {ytexture, cbtexture, crtexture};
|
||||
|
||||
// Bind the textures to their respective texture units.
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (videoTextureUnits[i] != 0)
|
||||
{
|
||||
// Store texture id so it can be re-bound later.
|
||||
activeTexUnits[videoTextureUnits[i] - 1] = textures[i];
|
||||
gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
gl.setTextureUnit(0);
|
||||
}
|
||||
|
||||
void Shader::checkSetScreenParams()
|
||||
{
|
||||
OpenGL::Viewport view = gl.getViewport();
|
||||
@@ -898,6 +955,9 @@ StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::built
|
||||
{"NormalMatrix", Shader::BUILTIN_NORMAL_MATRIX},
|
||||
{"love_PointSize", Shader::BUILTIN_POINT_SIZE},
|
||||
{"love_ScreenSize", Shader::BUILTIN_SCREEN_SIZE},
|
||||
{"love_VideoYChannel", Shader::BUILTIN_VIDEO_Y_CHANNEL},
|
||||
{"love_VideoCbChannel", Shader::BUILTIN_VIDEO_CB_CHANNEL},
|
||||
{"love_VideoCrChannel", Shader::BUILTIN_VIDEO_CR_CHANNEL},
|
||||
};
|
||||
|
||||
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
|
||||
|
||||
@@ -64,6 +64,9 @@ public:
|
||||
BUILTIN_NORMAL_MATRIX,
|
||||
BUILTIN_POINT_SIZE,
|
||||
BUILTIN_SCREEN_SIZE,
|
||||
BUILTIN_VIDEO_Y_CHANNEL,
|
||||
BUILTIN_VIDEO_CB_CHANNEL,
|
||||
BUILTIN_VIDEO_CR_CHANNEL,
|
||||
BUILTIN_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -89,9 +92,11 @@ public:
|
||||
|
||||
// Pointer to the default Shader.
|
||||
static Shader *defaultShader;
|
||||
static Shader *defaultVideoShader;
|
||||
|
||||
// Default shader code (a shader is always required internally.)
|
||||
static ShaderSource defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||
static ShaderSource defaultVideoCode[Graphics::RENDERER_MAX_ENUM];
|
||||
|
||||
/**
|
||||
* Creates a new Shader using a list of source codes.
|
||||
@@ -182,6 +187,7 @@ public:
|
||||
**/
|
||||
bool hasVertexAttrib(VertexAttribID attrib) const;
|
||||
|
||||
void setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture);
|
||||
void checkSetScreenParams();
|
||||
void checkSetPointSize(float size);
|
||||
void checkSetBuiltinUniforms();
|
||||
@@ -263,6 +269,8 @@ private:
|
||||
Matrix4 lastTransformMatrix;
|
||||
Matrix4 lastProjectionMatrix;
|
||||
|
||||
GLuint videoTextureUnits[3];
|
||||
|
||||
// Counts total number of textures bound to each texture unit in all shaders
|
||||
static std::vector<int> textureCounters;
|
||||
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "Video.h"
|
||||
|
||||
// LOVE
|
||||
#include "Shader.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Video::Video(love::video::VideoStream *stream)
|
||||
: stream(stream)
|
||||
, filter(Texture::getDefaultFilter())
|
||||
{
|
||||
filter.mipmap = Texture::FILTER_NONE;
|
||||
|
||||
stream->fillBackBuffer();
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
vertices[i].r = vertices[i].g = vertices[i].b = vertices[i].a = 255;
|
||||
|
||||
// Vertices are ordered for use with triangle strips:
|
||||
// 0----2
|
||||
// | / |
|
||||
// | / |
|
||||
// 1----3
|
||||
vertices[0].x = 0.0f;
|
||||
vertices[0].y = 0.0f;
|
||||
vertices[1].x = 0.0f;
|
||||
vertices[1].y = (float) stream->getHeight();
|
||||
vertices[2].x = (float) stream->getWidth();
|
||||
vertices[2].y = 0.0f;
|
||||
vertices[3].x = (float) stream->getWidth();
|
||||
vertices[3].y = (float) stream->getHeight();
|
||||
|
||||
vertices[0].s = 0.0f;
|
||||
vertices[0].t = 0.0f;
|
||||
vertices[1].s = 0.0f;
|
||||
vertices[1].t = 1.0f;
|
||||
vertices[2].s = 1.0f;
|
||||
vertices[2].t = 0.0f;
|
||||
vertices[3].s = 1.0f;
|
||||
vertices[3].t = 1.0f;
|
||||
|
||||
loadVolatile();
|
||||
}
|
||||
|
||||
Video::~Video()
|
||||
{
|
||||
unloadVolatile();
|
||||
}
|
||||
|
||||
bool Video::loadVolatile()
|
||||
{
|
||||
glGenTextures(3, &textures[0]);
|
||||
|
||||
// Create the textures using the initial frame data.
|
||||
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
||||
|
||||
gl.bindTexture(textures[0]);
|
||||
gl.setTextureFilter(filter);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->yw, frame->yh,
|
||||
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->yplane);
|
||||
|
||||
gl.bindTexture(textures[1]);
|
||||
gl.setTextureFilter(filter);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->cw, frame->ch,
|
||||
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->cbplane);
|
||||
|
||||
gl.bindTexture(textures[2]);
|
||||
gl.setTextureFilter(filter);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->cw, frame->ch,
|
||||
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->crplane);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Video::unloadVolatile()
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
gl.deleteTexture(textures[i]);
|
||||
textures[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
love::video::VideoStream *Video::getStream()
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
|
||||
void Video::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
update();
|
||||
|
||||
Shader *shader = Shader::current;
|
||||
bool defaultShader = (shader == Shader::defaultShader);
|
||||
if (defaultShader)
|
||||
{
|
||||
// If we're still using the default shader, substitute the video version
|
||||
Shader::defaultVideoShader->attach();
|
||||
shader = Shader::defaultVideoShader;
|
||||
}
|
||||
|
||||
shader->setVideoTextures(textures[0], textures[1], textures[2]);
|
||||
|
||||
OpenGL::TempTransform transform(gl);
|
||||
transform.get() *= Matrix4(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
|
||||
|
||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].x);
|
||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].s);
|
||||
|
||||
gl.prepareDraw();
|
||||
gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// If we were using the default shader, reattach it
|
||||
if (defaultShader)
|
||||
Shader::defaultShader->attach();
|
||||
}
|
||||
|
||||
void Video::update()
|
||||
{
|
||||
bool bufferschanged = stream->swapBuffers();
|
||||
stream->fillBackBuffer();
|
||||
|
||||
if (bufferschanged)
|
||||
{
|
||||
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
||||
|
||||
gl.bindTexture(textures[0]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->yw, frame->yh,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->yplane);
|
||||
|
||||
gl.bindTexture(textures[1]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->cw, frame->ch,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->cbplane);
|
||||
|
||||
gl.bindTexture(textures[2]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->cw, frame->ch,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->crplane);
|
||||
}
|
||||
}
|
||||
|
||||
love::audio::Source *Video::getSource()
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
void Video::setSource(love::audio::Source *source)
|
||||
{
|
||||
this->source = source;
|
||||
}
|
||||
|
||||
int Video::getWidth() const
|
||||
{
|
||||
return stream->getWidth();
|
||||
}
|
||||
|
||||
int Video::getHeight() const
|
||||
{
|
||||
return stream->getHeight();
|
||||
}
|
||||
|
||||
void Video::setFilter(const Texture::Filter &f)
|
||||
{
|
||||
if (!Texture::validateFilter(f, false))
|
||||
throw love::Exception("Invalid texture filter.");
|
||||
|
||||
filter = f;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
gl.bindTexture(textures[i]);
|
||||
gl.setTextureFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
const Texture::Filter &Video::getFilter() const
|
||||
{
|
||||
return filter;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/math.h"
|
||||
#include "graphics/Drawable.h"
|
||||
#include "graphics/Volatile.h"
|
||||
#include "video/VideoStream.h"
|
||||
#include "audio/Source.h"
|
||||
|
||||
#include "OpenGL.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class Video : public Drawable, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
Video(love::video::VideoStream *stream);
|
||||
~Video();
|
||||
|
||||
// Volatile
|
||||
bool loadVolatile();
|
||||
void unloadVolatile();
|
||||
|
||||
love::video::VideoStream *getStream();
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
love::audio::Source *getSource();
|
||||
void setSource(love::audio::Source *source);
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
void setFilter(const Texture::Filter &f);
|
||||
const Texture::Filter &getFilter() const;
|
||||
|
||||
private:
|
||||
|
||||
void update();
|
||||
|
||||
StrongRef<love::video::VideoStream> stream;
|
||||
StrongRef<love::audio::Source> source;
|
||||
|
||||
GLuint textures[3];
|
||||
|
||||
Vertex vertices[4];
|
||||
|
||||
Texture::Filter filter;
|
||||
|
||||
}; // Video
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "image/Image.h"
|
||||
#include "font/Rasterizer.h"
|
||||
#include "filesystem/wrap_Filesystem.h"
|
||||
#include "video/VideoStream.h"
|
||||
#include "image/wrap_Image.h"
|
||||
|
||||
#include <cassert>
|
||||
@@ -862,6 +863,20 @@ int w_newText(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_newVideo(lua_State *L)
|
||||
{
|
||||
if (!luax_istype(L, 1, VIDEO_VIDEO_STREAM_ID))
|
||||
luax_convobj(L, 1, "video", "newVideoStream");
|
||||
|
||||
auto stream = luax_checktype<love::video::VideoStream>(L, 1, VIDEO_VIDEO_STREAM_ID);
|
||||
Video *video = nullptr;
|
||||
|
||||
luax_catchexcept(L, [&]() { video = instance()->newVideo(stream); });
|
||||
luax_pushtype(L, GRAPHICS_VIDEO_ID, video);
|
||||
video->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_setColor(lua_State *L)
|
||||
{
|
||||
Colorf c;
|
||||
@@ -1274,25 +1289,37 @@ int w_setDefaultShaderCode(lua_State *L)
|
||||
lua_getfield(L, 1, "opengl");
|
||||
lua_rawgeti(L, -1, 1);
|
||||
lua_rawgeti(L, -2, 2);
|
||||
lua_rawgeti(L, -3, 3);
|
||||
|
||||
Shader::ShaderSource openglcode;
|
||||
openglcode.vertex = luax_checkstring(L, -2);
|
||||
openglcode.pixel = luax_checkstring(L, -1);
|
||||
openglcode.vertex = luax_checkstring(L, -3);
|
||||
openglcode.pixel = luax_checkstring(L, -2);
|
||||
|
||||
lua_pop(L, 3);
|
||||
Shader::ShaderSource openglVideocode;
|
||||
openglVideocode.vertex = luax_checkstring(L, -3);
|
||||
openglVideocode.pixel = luax_checkstring(L, -1);
|
||||
|
||||
lua_pop(L, 4);
|
||||
|
||||
lua_getfield(L, 1, "opengles");
|
||||
lua_rawgeti(L, -1, 1);
|
||||
lua_rawgeti(L, -2, 2);
|
||||
lua_rawgeti(L, -3, 3);
|
||||
|
||||
Shader::ShaderSource openglescode;
|
||||
openglescode.vertex = luax_checkstring(L, -2);
|
||||
openglescode.pixel = luax_checkstring(L, -1);
|
||||
openglescode.vertex = luax_checkstring(L, -3);
|
||||
openglescode.pixel = luax_checkstring(L, -2);
|
||||
|
||||
lua_pop(L, 3);
|
||||
Shader::ShaderSource openglesVideocode;
|
||||
openglesVideocode.vertex = luax_checkstring(L, -3);
|
||||
openglesVideocode.pixel = luax_checkstring(L, -1);
|
||||
|
||||
lua_pop(L, 4);
|
||||
|
||||
Shader::defaultCode[Graphics::RENDERER_OPENGL] = openglcode;
|
||||
Shader::defaultCode[Graphics::RENDERER_OPENGLES] = openglescode;
|
||||
Shader::defaultVideoCode[Graphics::RENDERER_OPENGL] = openglVideocode;
|
||||
Shader::defaultVideoCode[Graphics::RENDERER_OPENGLES] = openglesVideocode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1863,6 +1890,7 @@ static const luaL_Reg functions[] =
|
||||
{ "newShader", w_newShader },
|
||||
{ "newMesh", w_newMesh },
|
||||
{ "newText", w_newText },
|
||||
{ "_newVideo", w_newVideo },
|
||||
|
||||
{ "setColor", w_setColor },
|
||||
{ "getColor", w_getColor },
|
||||
@@ -1965,6 +1993,7 @@ static const lua_CFunction types[] =
|
||||
luaopen_shader,
|
||||
luaopen_mesh,
|
||||
luaopen_text,
|
||||
luaopen_video,
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "wrap_Shader.h"
|
||||
#include "wrap_Mesh.h"
|
||||
#include "wrap_Text.h"
|
||||
#include "wrap_Video.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
|
||||
@@ -165,6 +165,28 @@ varying mediump vec4 VaryingColor;
|
||||
|
||||
uniform sampler2D _tex0_;]],
|
||||
|
||||
FUNCTIONS = [[
|
||||
uniform sampler2D love_VideoYChannel;
|
||||
uniform sampler2D love_VideoCbChannel;
|
||||
uniform sampler2D love_VideoCrChannel;
|
||||
|
||||
vec4 VideoTexel(vec2 texcoords)
|
||||
{
|
||||
vec3 yuv;
|
||||
yuv[0] = Texel(love_VideoYChannel, texcoords).r;
|
||||
yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
|
||||
yuv[2] = Texel(love_VideoCrChannel, texcoords).r;
|
||||
yuv += vec3(-0.0627451017, -0.501960814, -0.501960814);
|
||||
|
||||
vec4 color;
|
||||
color.r = dot(yuv, vec3(1.164, 0.000, 1.596));
|
||||
color.g = dot(yuv, vec3(1.164, -0.391, -0.813));
|
||||
color.b = dot(yuv, vec3(1.164, 2.018, 0.000));
|
||||
color.a = 1.0;
|
||||
|
||||
return gammaCorrectColor(color);
|
||||
}]],
|
||||
|
||||
FOOTER = [[
|
||||
void main() {
|
||||
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
||||
@@ -209,6 +231,7 @@ local function createPixelCode(pixelcode, is_multicanvas, lang)
|
||||
love.graphics.isGammaCorrect() and "#define LOVE_GAMMA_CORRECT 1" or "",
|
||||
GLSL.PIXEL.HEADER, GLSL.UNIFORMS,
|
||||
GLSL.FUNCTIONS,
|
||||
GLSL.PIXEL.FUNCTIONS,
|
||||
lang == "glsles" and "#line 1" or "#line 0",
|
||||
pixelcode,
|
||||
is_multicanvas and GLSL.PIXEL.FOOTER_MULTI_CANVAS or GLSL.PIXEL.FOOTER,
|
||||
@@ -309,21 +332,45 @@ vec4 position(mat4 transform_proj, vec4 vertpos) {
|
||||
pixel = [[
|
||||
vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||
return Texel(tex, texcoord) * vcolor;
|
||||
}]]
|
||||
}]],
|
||||
videopixel = [[
|
||||
vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||
return VideoTexel(texcoord) * vcolor;
|
||||
}]],
|
||||
}
|
||||
|
||||
local defaults = {
|
||||
opengl = {
|
||||
createVertexCode(defaultcode.vertex, "glsl"),
|
||||
createPixelCode(defaultcode.pixel, false, "glsl"),
|
||||
createPixelCode(defaultcode.videopixel, false, "glsl"),
|
||||
},
|
||||
opengles = {
|
||||
createVertexCode(defaultcode.vertex, "glsles"),
|
||||
createPixelCode(defaultcode.pixel, false, "glsles"),
|
||||
createPixelCode(defaultcode.videopixel, false, "glsles"),
|
||||
},
|
||||
}
|
||||
|
||||
love.graphics._setDefaultShaderCode(defaults)
|
||||
|
||||
function love.graphics.newVideo(file, loadaudio)
|
||||
local video = love.graphics._newVideo(file)
|
||||
local source, success
|
||||
|
||||
if loadaudio ~= false then
|
||||
success, source = pcall(love.audio.newSource, video:getStream():getFilename())
|
||||
end
|
||||
if success then
|
||||
video:setSource(source)
|
||||
elseif loadaudio == true then
|
||||
error("Video had no audio track", 2)
|
||||
else
|
||||
video:getStream():setSync(love.video.newRemote())
|
||||
end
|
||||
|
||||
return video
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "wrap_Video.h"
|
||||
|
||||
// Shove the wrap_Video.lua code directly into a raw string literal.
|
||||
static const char video_lua[] =
|
||||
#include "wrap_Video.lua"
|
||||
;
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Video *luax_checkvideo(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Video>(L, idx, GRAPHICS_VIDEO_ID);
|
||||
}
|
||||
|
||||
int w_Video_getStream(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
luax_pushtype(L, VIDEO_VIDEO_STREAM_ID, video->getStream());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_getSource(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
auto source = video->getSource();
|
||||
if (source)
|
||||
luax_pushtype(L, AUDIO_SOURCE_ID, video->getSource());
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_setSource(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
if (lua_isnoneornil(L, 2))
|
||||
video->setSource(nullptr);
|
||||
else
|
||||
{
|
||||
auto source = luax_checktype<love::audio::Source>(L, 2, AUDIO_SOURCE_ID);
|
||||
video->setSource(source);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Video_getWidth(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
lua_pushnumber(L, video->getWidth());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_getHeight(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
lua_pushnumber(L, video->getHeight());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_getDimensions(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
lua_pushnumber(L, video->getWidth());
|
||||
lua_pushnumber(L, video->getHeight());
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_Video_setFilter(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
Texture::Filter f = video->getFilter();
|
||||
|
||||
const char *minstr = luaL_checkstring(L, 2);
|
||||
const char *magstr = luaL_optstring(L, 3, minstr);
|
||||
|
||||
if (!Texture::getConstant(minstr, f.min))
|
||||
return luaL_error(L, "Invalid filter mode: %s", minstr);
|
||||
if (!Texture::getConstant(magstr, f.mag))
|
||||
return luaL_error(L, "Invalid filter mode: %s", magstr);
|
||||
|
||||
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
|
||||
|
||||
luax_catchexcept(L, [&](){ video->setFilter(f); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Video_getFilter(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
const Texture::Filter f = video->getFilter();
|
||||
|
||||
const char *minstr = nullptr;
|
||||
const char *magstr = nullptr;
|
||||
|
||||
if (!Texture::getConstant(f.min, minstr))
|
||||
return luaL_error(L, "Unknown filter mode.");
|
||||
if (!Texture::getConstant(f.mag, magstr))
|
||||
return luaL_error(L, "Unknown filter mode.");
|
||||
|
||||
lua_pushstring(L, minstr);
|
||||
lua_pushstring(L, magstr);
|
||||
lua_pushnumber(L, f.anisotropy);
|
||||
return 3;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "getStream", w_Video_getStream },
|
||||
{ "getSource", w_Video_getSource },
|
||||
{ "_setSource", w_Video_setSource },
|
||||
{ "getWidth", w_Video_getWidth },
|
||||
{ "getHeight", w_Video_getHeight },
|
||||
{ "getDimensions", w_Video_getDimensions },
|
||||
{ "setFilter", w_Video_setFilter },
|
||||
{ "getFilter", w_Video_getFilter },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
int luaopen_video(lua_State *L)
|
||||
{
|
||||
int ret = luax_register_type(L, GRAPHICS_VIDEO_ID, "Video", functions, nullptr);
|
||||
|
||||
luaL_loadbuffer(L, video_lua, sizeof(video_lua), "Video.lua");
|
||||
luax_gettypemetatable(L, GRAPHICS_VIDEO_ID);
|
||||
lua_call(L, 1, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "Video.h"
|
||||
#include "common/runtime.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
int luaopen_video(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,69 @@
|
||||
R"luastring"--(
|
||||
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
|
||||
-- There is a matching delimiter at the bottom of the file.
|
||||
|
||||
--[[
|
||||
Copyright (c) 2006-2015 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
--]]
|
||||
|
||||
local Video_mt = ...
|
||||
local Video = Video_mt.__index
|
||||
|
||||
function Video:loadRemote()
|
||||
local stream = self:getStream()
|
||||
local remote = love.video.newRemote()
|
||||
stream:setSync(remote)
|
||||
return remote
|
||||
end
|
||||
|
||||
function Video:setSource(source)
|
||||
self:_setSource(source)
|
||||
if source then
|
||||
self:getStream():setSync(source)
|
||||
else
|
||||
self:getStream():setSync(love.video.newRemote())
|
||||
end
|
||||
end
|
||||
|
||||
function Video:play()
|
||||
return self:getStream():play()
|
||||
end
|
||||
|
||||
function Video:pause()
|
||||
return self:getStream():pause()
|
||||
end
|
||||
|
||||
function Video:seek(offset)
|
||||
return self:getStream():seek(offset)
|
||||
end
|
||||
|
||||
function Video:rewind()
|
||||
return self:getStream():rewind()
|
||||
end
|
||||
|
||||
function Video:tell()
|
||||
return self:getStream():tell()
|
||||
end
|
||||
|
||||
function Video:isPlaying()
|
||||
return self:getStream():isPlaying()
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
@@ -18,6 +18,8 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "common/config.h"
|
||||
|
||||
#include "Image.h"
|
||||
|
||||
#include "ImageData.h"
|
||||
|
||||
@@ -118,6 +118,9 @@ extern "C"
|
||||
#if defined(LOVE_ENABLE_TOUCH)
|
||||
extern int luaopen_love_touch(lua_State*);
|
||||
#endif
|
||||
#if defined(LOVE_ENABLE_VIDEO)
|
||||
extern int luaopen_love_video(lua_State*);
|
||||
#endif
|
||||
#if defined(LOVE_ENABLE_WINDOW)
|
||||
extern int luaopen_love_window(lua_State*);
|
||||
#endif
|
||||
@@ -174,6 +177,9 @@ static const luaL_Reg modules[] = {
|
||||
#if defined(LOVE_ENABLE_TOUCH)
|
||||
{ "love.touch", luaopen_love_touch },
|
||||
#endif
|
||||
#if defined(LOVE_ENABLE_VIDEO)
|
||||
{ "love.video", luaopen_love_video },
|
||||
#endif
|
||||
#if defined(LOVE_ENABLE_WINDOW)
|
||||
{ "love.window", luaopen_love_window },
|
||||
#endif
|
||||
|
||||
@@ -168,7 +168,7 @@ bool VorbisDecoder::accepts(const std::string &ext)
|
||||
{
|
||||
static const std::string supported[] =
|
||||
{
|
||||
"ogg", "oga", ""
|
||||
"ogg", "oga", "ogv", ""
|
||||
};
|
||||
|
||||
for (int i = 0; !(supported[i].empty()); i++)
|
||||
|
||||
@@ -56,7 +56,6 @@ Timer::Timer()
|
||||
, fpsUpdateFrequency(1)
|
||||
, frames(0)
|
||||
, dt(0)
|
||||
, timerPeriod(getTimerPeriod())
|
||||
{
|
||||
prevFpsUpdate = currTime = getTime();
|
||||
}
|
||||
@@ -115,8 +114,11 @@ double Timer::getTimerPeriod()
|
||||
return 0;
|
||||
}
|
||||
|
||||
double Timer::getTime() const
|
||||
double Timer::getTimeSinceEpoch()
|
||||
{
|
||||
// The timer period (reciprocal of the frequency.)
|
||||
static const double timerPeriod = getTimerPeriod();
|
||||
|
||||
#if defined(LOVE_LINUX)
|
||||
double mt;
|
||||
// Check for POSIX timers and monotonic clocks. If not supported, use the gettimeofday fallback.
|
||||
@@ -143,5 +145,10 @@ double Timer::getTime() const
|
||||
#endif
|
||||
}
|
||||
|
||||
double Timer::getTime() const
|
||||
{
|
||||
return getTimeSinceEpoch();
|
||||
}
|
||||
|
||||
} // timer
|
||||
} // love
|
||||
|
||||
@@ -77,6 +77,7 @@ public:
|
||||
* @return The time (in seconds)
|
||||
**/
|
||||
virtual double getTime() const;
|
||||
static double getTimeSinceEpoch();
|
||||
|
||||
private:
|
||||
|
||||
@@ -98,9 +99,6 @@ private:
|
||||
// The current timestep.
|
||||
double dt;
|
||||
|
||||
// The timer period (reciprocal of the frequency.)
|
||||
const double timerPeriod;
|
||||
|
||||
// Returns the timer period on some platforms.
|
||||
static double getTimerPeriod();
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_VIDEO_VIDEO_H
|
||||
#define LOVE_VIDEO_VIDEO_H
|
||||
|
||||
// LOVE
|
||||
#include "common/Module.h"
|
||||
#include "common/Stream.h"
|
||||
#include "filesystem/File.h"
|
||||
|
||||
#include "VideoStream.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
class Video : public Module
|
||||
{
|
||||
public:
|
||||
virtual ~Video() {}
|
||||
|
||||
// Implements Module
|
||||
virtual ModuleType getModuleType() const { return M_VIDEO; }
|
||||
|
||||
/**
|
||||
* Create a VideoStream representing video frames
|
||||
**/
|
||||
virtual VideoStream *newVideoStream(love::filesystem::File *file) = 0;
|
||||
}; // Video
|
||||
|
||||
} // video
|
||||
} // love
|
||||
|
||||
#endif // LOVE_VIDEO_VIDEO_H
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "VideoStream.h"
|
||||
|
||||
using love::thread::Lock;
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
void VideoStream::setSync(VideoStream::FrameSync *frameSync)
|
||||
{
|
||||
this->frameSync = frameSync;
|
||||
}
|
||||
|
||||
VideoStream::FrameSync *VideoStream::getSync() const
|
||||
{
|
||||
return frameSync;
|
||||
}
|
||||
|
||||
void VideoStream::play()
|
||||
{
|
||||
frameSync->play();
|
||||
}
|
||||
|
||||
void VideoStream::pause()
|
||||
{
|
||||
frameSync->pause();
|
||||
}
|
||||
|
||||
void VideoStream::seek(double offset)
|
||||
{
|
||||
frameSync->seek(offset);
|
||||
}
|
||||
|
||||
double VideoStream::tell()
|
||||
{
|
||||
return frameSync->tell();
|
||||
}
|
||||
|
||||
bool VideoStream::isPlaying()
|
||||
{
|
||||
return frameSync->isPlaying();
|
||||
}
|
||||
|
||||
VideoStream::Frame::Frame()
|
||||
: yplane(nullptr)
|
||||
, cbplane(nullptr)
|
||||
, crplane(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
VideoStream::Frame::~Frame()
|
||||
{
|
||||
delete[] yplane;
|
||||
delete[] cbplane;
|
||||
delete[] crplane;
|
||||
}
|
||||
|
||||
void VideoStream::FrameSync::copyState(const VideoStream::FrameSync *other)
|
||||
{
|
||||
seek(other->tell());
|
||||
if (other->isPlaying())
|
||||
play();
|
||||
else
|
||||
pause();
|
||||
}
|
||||
|
||||
double VideoStream::FrameSync::tell() const
|
||||
{
|
||||
return getPosition();
|
||||
}
|
||||
|
||||
VideoStream::DeltaSync::DeltaSync()
|
||||
: playing(false)
|
||||
, position(0)
|
||||
, speed(1)
|
||||
{
|
||||
}
|
||||
|
||||
VideoStream::DeltaSync::~DeltaSync()
|
||||
{
|
||||
}
|
||||
|
||||
double VideoStream::DeltaSync::getPosition() const
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
void VideoStream::DeltaSync::update(double dt)
|
||||
{
|
||||
Lock l(mutex);
|
||||
if (playing)
|
||||
position += dt*speed;
|
||||
}
|
||||
|
||||
void VideoStream::DeltaSync::play()
|
||||
{
|
||||
playing = true;
|
||||
}
|
||||
|
||||
void VideoStream::DeltaSync::pause()
|
||||
{
|
||||
playing = false;
|
||||
}
|
||||
|
||||
void VideoStream::DeltaSync::seek(double time)
|
||||
{
|
||||
Lock l(mutex);
|
||||
position = time;
|
||||
}
|
||||
|
||||
bool VideoStream::DeltaSync::isPlaying() const
|
||||
{
|
||||
return playing;
|
||||
}
|
||||
|
||||
VideoStream::SourceSync::SourceSync(love::audio::Source *source)
|
||||
: source(source)
|
||||
{
|
||||
}
|
||||
|
||||
double VideoStream::SourceSync::getPosition() const
|
||||
{
|
||||
return source->tell(love::audio::Source::UNIT_SECONDS);
|
||||
}
|
||||
|
||||
void VideoStream::SourceSync::play()
|
||||
{
|
||||
source->play();
|
||||
}
|
||||
|
||||
void VideoStream::SourceSync::pause()
|
||||
{
|
||||
source->pause();
|
||||
}
|
||||
|
||||
void VideoStream::SourceSync::seek(double time)
|
||||
{
|
||||
source->seek(time, love::audio::Source::UNIT_SECONDS);
|
||||
}
|
||||
|
||||
bool VideoStream::SourceSync::isPlaying() const
|
||||
{
|
||||
return !source->isStopped() && !source->isPaused();
|
||||
}
|
||||
|
||||
} // video
|
||||
} // love
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_VIDEO_VIDEOSTREAM_H
|
||||
#define LOVE_VIDEO_VIDEOSTREAM_H
|
||||
|
||||
// LOVE
|
||||
#include "common/Stream.h"
|
||||
#include "audio/Source.h"
|
||||
#include "thread/threads.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
class VideoStream : public Stream
|
||||
{
|
||||
public:
|
||||
virtual ~VideoStream() {}
|
||||
|
||||
virtual int getWidth() const = 0;
|
||||
virtual int getHeight() const = 0;
|
||||
virtual const std::string &getFilename() const = 0;
|
||||
|
||||
// Playback api
|
||||
virtual void play();
|
||||
virtual void pause();
|
||||
virtual void seek(double offset);
|
||||
virtual double tell();
|
||||
virtual bool isPlaying();
|
||||
|
||||
class FrameSync;
|
||||
class DeltaSync;
|
||||
|
||||
// The stream now owns the sync, do not reuse or free
|
||||
virtual void setSync(FrameSync *frameSync);
|
||||
virtual FrameSync *getSync() const;
|
||||
|
||||
// Data structures
|
||||
struct Frame
|
||||
{
|
||||
Frame();
|
||||
~Frame();
|
||||
|
||||
int yw, yh;
|
||||
unsigned char *yplane;
|
||||
|
||||
int cw, ch;
|
||||
unsigned char *cbplane;
|
||||
unsigned char *crplane;
|
||||
};
|
||||
|
||||
class FrameSync : public Object
|
||||
{
|
||||
public:
|
||||
virtual double getPosition() const = 0;
|
||||
virtual void update(double /*dt*/) {}
|
||||
virtual ~FrameSync() {}
|
||||
|
||||
void copyState(const FrameSync *other);
|
||||
|
||||
// Playback api
|
||||
virtual void play() = 0;
|
||||
virtual void pause() = 0;
|
||||
virtual void seek(double offset) = 0;
|
||||
virtual double tell() const;
|
||||
virtual bool isPlaying() const = 0;
|
||||
};
|
||||
|
||||
class DeltaSync : public FrameSync
|
||||
{
|
||||
public:
|
||||
DeltaSync();
|
||||
~DeltaSync();
|
||||
|
||||
virtual double getPosition() const override;
|
||||
virtual void update(double dt) override;
|
||||
|
||||
virtual void play() override;
|
||||
virtual void pause() override;
|
||||
virtual void seek(double time) override;
|
||||
virtual bool isPlaying() const override;
|
||||
|
||||
private:
|
||||
bool playing;
|
||||
double position;
|
||||
double speed;
|
||||
love::thread::MutexRef mutex;
|
||||
};
|
||||
|
||||
class SourceSync : public FrameSync
|
||||
{
|
||||
public:
|
||||
SourceSync(love::audio::Source *source);
|
||||
|
||||
virtual double getPosition() const override;
|
||||
virtual void play() override;
|
||||
virtual void pause() override;
|
||||
virtual void seek(double time) override;
|
||||
virtual bool isPlaying() const override;
|
||||
|
||||
private:
|
||||
StrongRef<love::audio::Source> source;
|
||||
};
|
||||
|
||||
protected:
|
||||
StrongRef<FrameSync> frameSync;
|
||||
};
|
||||
|
||||
} // video
|
||||
} // love
|
||||
|
||||
#endif // LOVE_VIDEO_VIDEOSTREAM_H
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// STL
|
||||
#include <vector>
|
||||
|
||||
// LOVE
|
||||
#include "Video.h"
|
||||
#include "common/delay.h"
|
||||
#include "timer/Timer.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
namespace theora
|
||||
{
|
||||
|
||||
Video::Video()
|
||||
{
|
||||
workerThread = new Worker();
|
||||
workerThread->start();
|
||||
}
|
||||
|
||||
Video::~Video()
|
||||
{
|
||||
delete workerThread;
|
||||
}
|
||||
|
||||
VideoStream *Video::newVideoStream(love::filesystem::File *file)
|
||||
{
|
||||
VideoStream *stream = new VideoStream(file);
|
||||
workerThread->addStream(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
const char *Video::getName() const
|
||||
{
|
||||
return "love.video.theora";
|
||||
}
|
||||
|
||||
Worker::Worker()
|
||||
: stopping(false)
|
||||
{
|
||||
threadName = "VideoWorker";
|
||||
}
|
||||
|
||||
Worker::~Worker()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void Worker::addStream(VideoStream *stream)
|
||||
{
|
||||
love::thread::Lock l(mutex);
|
||||
streams.push_back(stream);
|
||||
}
|
||||
|
||||
void Worker::stop()
|
||||
{
|
||||
{
|
||||
love::thread::Lock l(mutex);
|
||||
stopping = true;
|
||||
}
|
||||
|
||||
owner->wait();
|
||||
}
|
||||
|
||||
void Worker::threadFunction()
|
||||
{
|
||||
double lastFrame = love::timer::Timer::getTimeSinceEpoch();
|
||||
while (true)
|
||||
{
|
||||
double curFrame = love::timer::Timer::getTimeSinceEpoch();
|
||||
double dt = curFrame-lastFrame;
|
||||
lastFrame = curFrame;
|
||||
|
||||
{
|
||||
love::thread::Lock l(mutex);
|
||||
|
||||
if (stopping)
|
||||
return;
|
||||
|
||||
for (auto it = streams.begin(); it != streams.end(); ++it)
|
||||
{
|
||||
VideoStream *stream = *it;
|
||||
if (stream->getReferenceCount() == 1)
|
||||
{
|
||||
// We're the only ones left
|
||||
streams.erase(it);
|
||||
break;
|
||||
}
|
||||
|
||||
stream->threadedFillBackBuffer(dt);
|
||||
}
|
||||
}
|
||||
|
||||
// sleep
|
||||
love::delay(2);
|
||||
}
|
||||
}
|
||||
|
||||
} // theora
|
||||
} // video
|
||||
} // love
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_VIDEO_THEORA_VIDEO_H
|
||||
#define LOVE_VIDEO_THEORA_VIDEO_H
|
||||
|
||||
// STL
|
||||
#include <vector>
|
||||
|
||||
// LOVE
|
||||
#include "filesystem/File.h"
|
||||
#include "video/Video.h"
|
||||
#include "thread/threads.h"
|
||||
#include "VideoStream.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
namespace theora
|
||||
{
|
||||
|
||||
class Worker;
|
||||
|
||||
class Video : public love::video::Video
|
||||
{
|
||||
public:
|
||||
Video();
|
||||
~Video();
|
||||
|
||||
// Implements Module
|
||||
virtual const char *getName() const;
|
||||
|
||||
VideoStream *newVideoStream(love::filesystem::File* file);
|
||||
|
||||
private:
|
||||
Worker *workerThread;
|
||||
}; // Video
|
||||
|
||||
class Worker : public love::thread::Threadable
|
||||
{
|
||||
public:
|
||||
Worker();
|
||||
~Worker();
|
||||
|
||||
// Implements Threadable
|
||||
void threadFunction();
|
||||
|
||||
void addStream(VideoStream *stream);
|
||||
// Frees itself!
|
||||
void stop();
|
||||
|
||||
private:
|
||||
std::vector<StrongRef<VideoStream>> streams;
|
||||
love::thread::MutexRef mutex;
|
||||
|
||||
volatile bool stopping;
|
||||
}; // Worker
|
||||
|
||||
} // theora
|
||||
} // video
|
||||
} // love
|
||||
|
||||
#endif // LOVE_VIDEO_THEORA_VIDEO_H
|
||||
@@ -0,0 +1,402 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// STL
|
||||
#include <iostream>
|
||||
|
||||
// LOVE
|
||||
#include "VideoStream.h"
|
||||
|
||||
using love::filesystem::File;
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
namespace theora
|
||||
{
|
||||
|
||||
VideoStream::VideoStream(love::filesystem::File *file)
|
||||
: file(file)
|
||||
, headerParsed(false)
|
||||
, streamInited(false)
|
||||
, videoSerial(0)
|
||||
, decoder(nullptr)
|
||||
, frameReady(false)
|
||||
, lastFrame(0)
|
||||
, nextFrame(0)
|
||||
, eos(false)
|
||||
, lagCounter(0)
|
||||
{
|
||||
ogg_sync_init(&sync);
|
||||
th_info_init(&videoInfo);
|
||||
|
||||
frontBuffer = new Frame();
|
||||
backBuffer = new Frame();
|
||||
|
||||
try
|
||||
{
|
||||
parseHeader();
|
||||
}
|
||||
catch (love::Exception &ex)
|
||||
{
|
||||
delete backBuffer;
|
||||
delete frontBuffer;
|
||||
th_info_clear(&videoInfo);
|
||||
ogg_sync_clear(&sync);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
frameSync = new DeltaSync();
|
||||
frameSync->release();
|
||||
}
|
||||
|
||||
VideoStream::~VideoStream()
|
||||
{
|
||||
if (decoder)
|
||||
th_decode_free(decoder);
|
||||
|
||||
th_info_clear(&videoInfo);
|
||||
if (headerParsed)
|
||||
ogg_stream_clear(&stream);
|
||||
|
||||
ogg_sync_clear(&sync);
|
||||
|
||||
delete frontBuffer;
|
||||
delete backBuffer;
|
||||
}
|
||||
|
||||
int VideoStream::getWidth() const
|
||||
{
|
||||
if (headerParsed)
|
||||
return videoInfo.pic_width;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VideoStream::getHeight() const
|
||||
{
|
||||
if (headerParsed)
|
||||
return videoInfo.pic_height;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string &VideoStream::getFilename() const
|
||||
{
|
||||
return file->getFilename();
|
||||
}
|
||||
|
||||
void VideoStream::setSync(FrameSync *frameSync)
|
||||
{
|
||||
love::thread::Lock l(bufferMutex);
|
||||
this->frameSync = frameSync;
|
||||
}
|
||||
|
||||
const void *VideoStream::getFrontBuffer() const
|
||||
{
|
||||
return frontBuffer;
|
||||
}
|
||||
|
||||
size_t VideoStream::getSize() const
|
||||
{
|
||||
return sizeof(Frame);
|
||||
}
|
||||
|
||||
void VideoStream::readPage()
|
||||
{
|
||||
char *syncBuffer = nullptr;
|
||||
while (ogg_sync_pageout(&sync, &page) != 1)
|
||||
{
|
||||
if (syncBuffer && !headerParsed && ogg_stream_check(&stream))
|
||||
throw love::Exception("Invalid stream");
|
||||
|
||||
syncBuffer = ogg_sync_buffer(&sync, 8192);
|
||||
size_t read = file->read(syncBuffer, 8192);
|
||||
ogg_sync_wrote(&sync, read);
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoStream::readPacket(bool mustSucceed)
|
||||
{
|
||||
if (!streamInited)
|
||||
{
|
||||
readPage();
|
||||
videoSerial = ogg_page_serialno(&page);
|
||||
ogg_stream_init(&stream, videoSerial);
|
||||
streamInited = true;
|
||||
ogg_stream_pagein(&stream, &page);
|
||||
}
|
||||
|
||||
while (ogg_stream_packetout(&stream, &packet) != 1)
|
||||
{
|
||||
// We need to read another page, but there is none, we're at the end
|
||||
if (ogg_page_eos(&page) && !mustSucceed)
|
||||
return eos = true;
|
||||
|
||||
do
|
||||
{
|
||||
readPage();
|
||||
} while (ogg_page_serialno(&page) != videoSerial);
|
||||
|
||||
ogg_stream_pagein(&stream, &page);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void scaleFormat(th_pixel_fmt fmt, T &x, T &y)
|
||||
{
|
||||
switch(fmt)
|
||||
{
|
||||
case TH_PF_420:
|
||||
y /= 2;
|
||||
case TH_PF_422:
|
||||
x /= 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoStream::parseHeader()
|
||||
{
|
||||
if (headerParsed)
|
||||
return;
|
||||
|
||||
th_comment comment;
|
||||
th_setup_info *setupInfo = nullptr;
|
||||
th_comment_init(&comment);
|
||||
int ret;
|
||||
|
||||
do
|
||||
{
|
||||
readPacket();
|
||||
ret = th_decode_headerin(&videoInfo, &comment, &setupInfo, &packet);
|
||||
if (ret == TH_ENOTFORMAT)
|
||||
{
|
||||
ogg_stream_clear(&stream);
|
||||
streamInited = false;
|
||||
}
|
||||
} while(ret < 0 && !ogg_page_eos(&page));
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
th_comment_clear(&comment);
|
||||
throw love::Exception("Could not find header");
|
||||
}
|
||||
|
||||
while (ret > 0)
|
||||
{
|
||||
readPacket();
|
||||
ret = th_decode_headerin(&videoInfo, &comment, &setupInfo, &packet);
|
||||
}
|
||||
|
||||
th_comment_clear(&comment);
|
||||
|
||||
decoder = th_decode_alloc(&videoInfo, setupInfo);
|
||||
th_setup_free(setupInfo);
|
||||
|
||||
Frame *buffers[2] = {backBuffer, frontBuffer};
|
||||
|
||||
yPlaneXOffset = cPlaneXOffset = videoInfo.pic_x;
|
||||
yPlaneYOffset = cPlaneYOffset = videoInfo.pic_y;
|
||||
|
||||
scaleFormat(videoInfo.pixel_fmt, cPlaneXOffset, cPlaneYOffset);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
buffers[i]->cw = buffers[i]->yw = videoInfo.pic_width;
|
||||
buffers[i]->ch = buffers[i]->yh = videoInfo.pic_height;
|
||||
|
||||
scaleFormat(videoInfo.pixel_fmt, buffers[i]->cw, buffers[i]->ch);
|
||||
|
||||
buffers[i]->yplane = new unsigned char[buffers[i]->yw * buffers[i]->yh];
|
||||
buffers[i]->cbplane = new unsigned char[buffers[i]->cw * buffers[i]->ch];
|
||||
buffers[i]->crplane = new unsigned char[buffers[i]->cw * buffers[i]->ch];
|
||||
|
||||
memset(buffers[i]->yplane, 16, buffers[i]->yw * buffers[i]->yh);
|
||||
memset(buffers[i]->cbplane, 128, buffers[i]->cw * buffers[i]->ch);
|
||||
memset(buffers[i]->crplane, 128, buffers[i]->cw * buffers[i]->ch);
|
||||
}
|
||||
|
||||
headerParsed = true;
|
||||
th_decode_packetin(decoder, &packet, nullptr);
|
||||
}
|
||||
|
||||
// Arbitrary seeking isn't supported yet, but rewinding is
|
||||
void VideoStream::rewind()
|
||||
{
|
||||
// Seek our data stream back to the start
|
||||
file->seek(0);
|
||||
|
||||
// Break our sync, and discard the rest of the page
|
||||
ogg_sync_reset(&sync);
|
||||
ogg_sync_pageseek(&sync, &page);
|
||||
|
||||
// Read our first page/packet from the stream again
|
||||
readPacket(true);
|
||||
|
||||
// Now tell theora we're at frame 1 (not 0!)
|
||||
int64 granPos = 1;
|
||||
th_decode_ctl(decoder, TH_DECCTL_SET_GRANPOS, &granPos, sizeof(granPos));
|
||||
|
||||
// Force a redraw, since this will always be less than the sync's position
|
||||
lastFrame = nextFrame = -1;
|
||||
eos = false;
|
||||
}
|
||||
|
||||
void VideoStream::seekDecoder(double target)
|
||||
{
|
||||
double low = 0;
|
||||
double high = file->getSize();
|
||||
|
||||
while (high-low > 0.0001)
|
||||
{
|
||||
// Determine our next binary search position
|
||||
double pos = (high-low)/2+low;
|
||||
file->seek(pos);
|
||||
|
||||
// Break sync
|
||||
ogg_sync_reset(&sync);
|
||||
ogg_sync_pageseek(&sync, &page);
|
||||
|
||||
// Read a packet
|
||||
readPacket(true);
|
||||
|
||||
// Determine if this is the right place
|
||||
double curTime = th_granule_time(decoder, packet.granulepos);
|
||||
if (curTime > target && th_granule_time(decoder, packet.granulepos-1) < target)
|
||||
break;
|
||||
else if (curTime > target)
|
||||
high = pos;
|
||||
else
|
||||
low = pos;
|
||||
}
|
||||
|
||||
// Now update theora and our decoder on this new position of ours
|
||||
lastFrame = nextFrame = -1;
|
||||
eos = false;
|
||||
th_decode_ctl(decoder, TH_DECCTL_SET_GRANPOS, &packet.granulepos, sizeof(packet.granulepos));
|
||||
}
|
||||
|
||||
void VideoStream::threadedFillBackBuffer(double dt)
|
||||
{
|
||||
// Synchronize
|
||||
frameSync->update(dt);
|
||||
double position = frameSync->getPosition();
|
||||
|
||||
// Seeking backwards
|
||||
if (position < lastFrame)
|
||||
{
|
||||
if (position < 0.01)
|
||||
rewind();
|
||||
else
|
||||
seekDecoder(position);
|
||||
}
|
||||
|
||||
// If we're at the end of the stream, or if we're displaying the right frame
|
||||
// stop here
|
||||
if (eos || position < nextFrame)
|
||||
return;
|
||||
|
||||
th_ycbcr_buffer bufferinfo;
|
||||
th_decode_ycbcr_out(decoder, bufferinfo);
|
||||
|
||||
ogg_int64_t granulePosition;
|
||||
do
|
||||
{
|
||||
if (readPacket())
|
||||
return;
|
||||
} while (th_decode_packetin(decoder, &packet, &granulePosition) != 0);
|
||||
lastFrame = nextFrame;
|
||||
nextFrame = th_granule_time(decoder, granulePosition);
|
||||
|
||||
{
|
||||
// Don't swap whilst we're writing to the backbuffer
|
||||
love::thread::Lock l(bufferMutex);
|
||||
frameReady = false;
|
||||
}
|
||||
|
||||
for (int y = 0; y < backBuffer->yh; ++y)
|
||||
{
|
||||
memcpy(backBuffer->yplane+backBuffer->yw*y,
|
||||
bufferinfo[0].data+
|
||||
bufferinfo[0].stride*(y+yPlaneYOffset)+yPlaneXOffset,
|
||||
backBuffer->yw);
|
||||
}
|
||||
|
||||
for (int y = 0; y < backBuffer->ch; ++y)
|
||||
{
|
||||
memcpy(backBuffer->cbplane+backBuffer->cw*y,
|
||||
bufferinfo[1].data+
|
||||
bufferinfo[1].stride*(y+cPlaneYOffset)+cPlaneXOffset,
|
||||
backBuffer->cw);
|
||||
}
|
||||
|
||||
for (int y = 0; y < backBuffer->ch; ++y)
|
||||
{
|
||||
memcpy(backBuffer->crplane+backBuffer->cw*y,
|
||||
bufferinfo[2].data+
|
||||
bufferinfo[2].stride*(y+cPlaneYOffset)+cPlaneXOffset,
|
||||
backBuffer->cw);
|
||||
}
|
||||
|
||||
// Seeking forwards:
|
||||
// If we're still not on the right frame, either we're lagging or we're seeking
|
||||
// After 5 frames, go for a seek. This is not ideal.. but what is
|
||||
if (position > nextFrame)
|
||||
{
|
||||
if (++lagCounter > 5)
|
||||
seek(position);
|
||||
}
|
||||
else
|
||||
lagCounter = 0;
|
||||
|
||||
love::thread::Lock l(bufferMutex);
|
||||
frameReady = true;
|
||||
}
|
||||
|
||||
void VideoStream::fillBackBuffer()
|
||||
{
|
||||
// Done in worker thread
|
||||
}
|
||||
|
||||
bool VideoStream::swapBuffers()
|
||||
{
|
||||
if (eos)
|
||||
return false;
|
||||
|
||||
love::thread::Lock l(bufferMutex);
|
||||
if (!frameReady)
|
||||
return false;
|
||||
frameReady = false;
|
||||
|
||||
Frame *temp = frontBuffer;
|
||||
frontBuffer = backBuffer;
|
||||
backBuffer = temp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // theora
|
||||
} // video
|
||||
} // love
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_VIDEO_THEORA_VIDEOSTREAM_H
|
||||
#define LOVE_VIDEO_THEORA_VIDEOSTREAM_H
|
||||
|
||||
#include "video/VideoStream.h"
|
||||
|
||||
// LOVE
|
||||
#include "common/int.h"
|
||||
#include "filesystem/File.h"
|
||||
#include "thread/threads.h"
|
||||
|
||||
// OGG/Theora
|
||||
#include <ogg/ogg.h>
|
||||
#include <theora/codec.h>
|
||||
#include <theora/theoradec.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
namespace theora
|
||||
{
|
||||
|
||||
class VideoStream : public love::video::VideoStream
|
||||
{
|
||||
public:
|
||||
VideoStream(love::filesystem::File *file);
|
||||
~VideoStream();
|
||||
|
||||
const void *getFrontBuffer() const;
|
||||
size_t getSize() const;
|
||||
void fillBackBuffer();
|
||||
bool swapBuffers();
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
const std::string &getFilename() const;
|
||||
void setSync(FrameSync *frameSync);
|
||||
|
||||
void threadedFillBackBuffer(double dt);
|
||||
|
||||
private:
|
||||
StrongRef<love::filesystem::File> file;
|
||||
|
||||
bool headerParsed;
|
||||
bool streamInited;
|
||||
int videoSerial;
|
||||
ogg_sync_state sync;
|
||||
ogg_stream_state stream;
|
||||
ogg_page page;
|
||||
ogg_packet packet;
|
||||
|
||||
th_info videoInfo;
|
||||
th_dec_ctx *decoder;
|
||||
|
||||
Frame *frontBuffer;
|
||||
Frame *backBuffer;
|
||||
unsigned int yPlaneXOffset;
|
||||
unsigned int cPlaneXOffset;
|
||||
unsigned int yPlaneYOffset;
|
||||
unsigned int cPlaneYOffset;
|
||||
|
||||
love::thread::MutexRef bufferMutex;
|
||||
bool frameReady;
|
||||
|
||||
double lastFrame;
|
||||
double nextFrame;
|
||||
bool eos;
|
||||
unsigned int lagCounter;
|
||||
|
||||
void readPage();
|
||||
bool readPacket(bool mustSucceed = false); // true if eos
|
||||
void parseHeader();
|
||||
void rewind();
|
||||
void seekDecoder(double target);
|
||||
}; // VideoStream
|
||||
|
||||
} // theora
|
||||
} // video
|
||||
} // love
|
||||
|
||||
#endif // LOVE_VIDEO_THEORA_VIDEOSTREAM_H
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "filesystem/wrap_Filesystem.h"
|
||||
|
||||
#include "theora/Video.h"
|
||||
#include "wrap_Video.h"
|
||||
#include "wrap_VideoStream.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
#define instance() (Module::getInstance<Video>(Module::M_VIDEO))
|
||||
|
||||
int w_newVideoStream(lua_State *L)
|
||||
{
|
||||
love::filesystem::File *file = love::filesystem::luax_getfile(L, 1);
|
||||
|
||||
VideoStream *stream = nullptr;
|
||||
luax_catchexcept(L, [&]() {
|
||||
// Can't check if open for reading
|
||||
if (!file->isOpen() && !file->open(love::filesystem::File::MODE_READ))
|
||||
luaL_error(L, "File is not open and cannot be opened");
|
||||
|
||||
stream = instance()->newVideoStream(file);
|
||||
});
|
||||
|
||||
luax_pushtype(L, VIDEO_VIDEO_STREAM_ID, stream);
|
||||
stream->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const lua_CFunction types[] =
|
||||
{
|
||||
luaopen_videostream,
|
||||
0
|
||||
};
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "newVideoStream", w_newVideoStream },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_love_video(lua_State *L)
|
||||
{
|
||||
Video *instance = instance();
|
||||
if (instance == nullptr)
|
||||
{
|
||||
luax_catchexcept(L, [&](){ instance = new love::video::theora::Video(); });
|
||||
}
|
||||
else
|
||||
instance->retain();
|
||||
|
||||
WrappedModule w;
|
||||
w.module = instance;
|
||||
w.name = "video";
|
||||
w.type = MODULE_ID;
|
||||
w.functions = functions;
|
||||
w.types = types;
|
||||
|
||||
return luax_register_module(L, w);
|
||||
}
|
||||
|
||||
} // video
|
||||
} // love
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_VIDEO_WRAP_VIDEO_H
|
||||
#define LOVE_VIDEO_WRAP_VIDEO_H
|
||||
|
||||
// LOVE
|
||||
#include "VideoStream.h"
|
||||
#include "common/runtime.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
extern "C" LOVE_EXPORT int luaopen_love_video(lua_State *L);
|
||||
|
||||
} // video
|
||||
} // love
|
||||
|
||||
#endif // LOVE_VIDEO_WRAP_VIDEO_H
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "wrap_VideoStream.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
VideoStream *luax_checkvideostream(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<VideoStream>(L, idx, VIDEO_VIDEO_STREAM_ID);
|
||||
}
|
||||
|
||||
int w_VideoStream_setSync(lua_State *L)
|
||||
{
|
||||
auto stream = luax_checkvideostream(L, 1);
|
||||
|
||||
if (luax_istype(L, 2, AUDIO_SOURCE_ID))
|
||||
{
|
||||
auto src = luax_totype<love::audio::Source>(L, 2, AUDIO_SOURCE_ID);
|
||||
auto sync = new VideoStream::SourceSync(src);
|
||||
stream->setSync(sync);
|
||||
sync->release();
|
||||
}
|
||||
else if (luax_istype(L, 2, VIDEO_VIDEO_STREAM_ID))
|
||||
{
|
||||
auto other = luax_totype<VideoStream>(L, 2, VIDEO_VIDEO_STREAM_ID);
|
||||
stream->setSync(other->getSync());
|
||||
}
|
||||
else if (lua_isnoneornil(L, 2))
|
||||
{
|
||||
auto newSync = new VideoStream::DeltaSync();
|
||||
newSync->copyState(stream->getSync());
|
||||
stream->setSync(newSync);
|
||||
newSync->release();
|
||||
}
|
||||
else
|
||||
return luax_typerror(L, 2, "Source or VideoStream or nil");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_VideoStream_getFilename(lua_State *L)
|
||||
{
|
||||
auto stream = luax_checkvideostream(L, 1);
|
||||
luax_pushstring(L, stream->getFilename());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_VideoStream_play(lua_State *L)
|
||||
{
|
||||
auto stream = luax_checkvideostream(L, 1);
|
||||
stream->play();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_VideoStream_pause(lua_State *L)
|
||||
{
|
||||
auto stream = luax_checkvideostream(L, 1);
|
||||
stream->pause();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_VideoStream_seek(lua_State *L)
|
||||
{
|
||||
auto stream = luax_checkvideostream(L, 1);
|
||||
double offset = luaL_checknumber(L, 2);
|
||||
stream->seek(offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_VideoStream_rewind(lua_State *L)
|
||||
{
|
||||
auto stream = luax_checkvideostream(L, 1);
|
||||
stream->seek(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_VideoStream_tell(lua_State *L)
|
||||
{
|
||||
auto stream = luax_checkvideostream(L, 1);
|
||||
lua_pushnumber(L, stream->tell());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_VideoStream_isPlaying(lua_State *L)
|
||||
{
|
||||
auto stream = luax_checkvideostream(L, 1);
|
||||
luax_pushboolean(L, stream->isPlaying());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg videostream_functions[] =
|
||||
{
|
||||
{ "setSync", w_VideoStream_setSync },
|
||||
{ "getFilename", w_VideoStream_getFilename },
|
||||
{ "play", w_VideoStream_play },
|
||||
{ "pause", w_VideoStream_pause },
|
||||
{ "seek", w_VideoStream_seek },
|
||||
{ "rewind", w_VideoStream_rewind },
|
||||
{ "tell", w_VideoStream_tell },
|
||||
{ "isPlaying", w_VideoStream_isPlaying },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
int luaopen_videostream(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, VIDEO_VIDEO_STREAM_ID, "VideoStream", videostream_functions, nullptr);
|
||||
}
|
||||
|
||||
} // video
|
||||
} // love
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/runtime.h"
|
||||
#include "VideoStream.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
LOVE_EXPORT int luaopen_videostream(lua_State *L);
|
||||
|
||||
} // video
|
||||
} // love
|
||||
@@ -369,6 +369,7 @@ function love.init()
|
||||
font = true,
|
||||
thread = true,
|
||||
window = true,
|
||||
video = true,
|
||||
},
|
||||
console = false, -- Only relevant for windows.
|
||||
identity = false,
|
||||
@@ -425,6 +426,7 @@ function love.init()
|
||||
"system",
|
||||
"audio",
|
||||
"image",
|
||||
"video",
|
||||
"font",
|
||||
"window",
|
||||
"graphics",
|
||||
|
||||
@@ -675,6 +675,7 @@ const unsigned char boot_lua[] =
|
||||
0x09, 0x09, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x7d, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c,
|
||||
0x20, 0x2d, 0x2d, 0x20, 0x4f, 0x6e, 0x6c, 0x79, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x20,
|
||||
@@ -779,6 +780,7 @@ const unsigned char boot_lua[] =
|
||||
0x09, 0x09, 0x22, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x22, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x22, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x22, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x22, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x22, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x22, 0x66, 0x6f, 0x6e, 0x74, 0x22, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x22, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x22, 0x2c, 0x0a,
|
||||
0x09, 0x09, 0x22, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x22, 0x2c, 0x0a,
|
||||
|
||||
Reference in New Issue
Block a user