Add automation function to get frame

This commit is contained in:
sepro 2022-05-07 16:13:50 +02:00 committed by arch1t3cht
parent bde149fd30
commit 8e1cc6228e
4 changed files with 190 additions and 1 deletions

View file

@ -0,0 +1,69 @@
Video Frame functions in Automation 4
This file describes the interface used for reading frames from loaded videos.
---
Get a specific frame from the currently loaded video on which multiple other
functions are defined.
function aegisub.get_frame(frame_number, withSubtitles)
@frame_number (number)
Number of frame to retrieve.
@withSubtitles (boolean)
Optional. Whether to load with subtitles drawn on to the frame.
Returns: frame (userdata)
The frame object defines multiple other functions. See below.
---
Get width of frame object.
function frame:width()
Returns: number
Width in pixels.
---
Get height of frame object.
function frame:height()
Returns: number
Height in pixels.
---
Get RGB pixel value at a certain position of frame object.
function frame:frame:getPixel(x, y)
@x (number)
Pixel to retrieve on the x-axis
@y (number)
Pixel to retrieve on the y-axis
Returns: number
Integer value representing the RGB pixel value.
---
Get ASS formated pixel value at a certain position of frame object.
function frame:getPixelFormatted(x, y)
@x (number)
Pixel to retrieve on the x-axis
@y (number)
Pixel to retrieve on the y-axis
Returns: string
String in ASS format representing the pixel value. e.g. "&H0073FF&"
---

View file

@ -50,8 +50,9 @@
#include "project.h" #include "project.h"
#include "selection_controller.h" #include "selection_controller.h"
#include "subs_controller.h" #include "subs_controller.h"
#include "video_controller.h"
#include "text_selection_controller.h" #include "text_selection_controller.h"
#include "video_controller.h"
#include "video_frame.h"
#include "utils.h" #include "utils.h"
#include <libaegisub/dispatch.h> #include <libaegisub/dispatch.h>
@ -200,6 +201,116 @@ namespace {
} }
} }
std::shared_ptr<VideoFrame> check_VideoFrame(lua_State *L) {
auto framePtr = static_cast<std::shared_ptr<VideoFrame>*>(luaL_checkudata(L, 1, "VideoFrame"));
return *framePtr;
}
int FrameWidth(lua_State *L) {
std::shared_ptr<VideoFrame> frame = check_VideoFrame(L);
push_value(L, frame->width);
return 1;
}
int FrameHeight(lua_State *L) {
std::shared_ptr<VideoFrame> frame = check_VideoFrame(L);
push_value(L, frame->height);
return 1;
}
int FramePixel(lua_State *L) {
std::shared_ptr<VideoFrame> frame = check_VideoFrame(L);
size_t x = lua_tointeger(L, -2);
size_t y = lua_tointeger(L, -1);
lua_pop(L, 2);
if (x < frame->width && y < frame->height) {
if (frame->flipped)
y = frame->height - y;
size_t pos = y * frame->pitch + x * 4;
// VideoFrame is stored as BGRA, but we want to return RGB
int pixelValue = frame->data[pos+2] * 65536 + frame->data[pos+1] * 256 + frame->data[pos];
push_value(L, pixelValue);
} else {
lua_pushnil(L);
}
return 1;
}
int FramePixelFormatted(lua_State *L) {
std::shared_ptr<VideoFrame> frame = check_VideoFrame(L);
size_t x = lua_tointeger(L, -2);
size_t y = lua_tointeger(L, -1);
lua_pop(L, 2);
if (x < frame->width && y < frame->height) {
if (frame->flipped)
y = frame->height - y;
size_t pos = y * frame->pitch + x * 4;
// VideoFrame is stored as BGRA, Color expects RGBA
agi::Color* color = new agi::Color(frame->data[pos+2], frame->data[pos+1], frame->data[pos], frame->data[pos+3]);
push_value(L, color->GetAssOverrideFormatted());
} else {
lua_pushnil(L);
}
return 1;
}
int FrameDestory(lua_State *L) {
std::shared_ptr<VideoFrame> frame = check_VideoFrame(L);
frame.~shared_ptr<VideoFrame>();
return 0;
}
int get_frame(lua_State *L)
{
// get frame number from stack
const agi::Context *c = get_context(L);
int frameNumber = lua_tointeger(L, 1);
bool withSubtitles = false;
if (lua_gettop(L) >= 2) {
withSubtitles = lua_toboolean(L, 2);
lua_pop(L, 1);
}
lua_pop(L, 1);
static const struct luaL_Reg FrameTableDefinition [] = {
{"width", FrameWidth},
{"height", FrameHeight},
{"getPixel", FramePixel},
{"getPixelFormatted", FramePixelFormatted},
{"__gc", FrameDestory},
{NULL, NULL}
};
// create and register metatable if not already done
if (luaL_newmetatable(L, "VideoFrame")) {
// metatable.__index = metatable
lua_pushstring(L, "__index");
lua_pushvalue(L, -2);
lua_settable(L, -3);
luaL_register(L, NULL, FrameTableDefinition);
}
if (c && c->project->Timecodes().IsLoaded()) {
std::shared_ptr<VideoFrame> frame = c->videoController->GetFrame(frameNumber, !withSubtitles);
void *userData = lua_newuserdata(L, sizeof(std::shared_ptr<VideoFrame>));
new(userData) std::shared_ptr<VideoFrame>(frame);
luaL_getmetatable(L, "VideoFrame");
lua_setmetatable(L, -2);
} else {
lua_pushnil(L);
}
return 1;
}
int get_keyframes(lua_State *L) int get_keyframes(lua_State *L)
{ {
if (const agi::Context *c = get_context(L)) if (const agi::Context *c = get_context(L))
@ -529,6 +640,7 @@ namespace {
set_field<project_properties>(L, "project_properties"); set_field<project_properties>(L, "project_properties");
set_field<lua_get_audio_selection>(L, "get_audio_selection"); set_field<lua_get_audio_selection>(L, "get_audio_selection");
set_field<lua_set_status_text>(L, "set_status_text"); set_field<lua_set_status_text>(L, "set_status_text");
set_field<get_frame>(L, "get_frame");
lua_createtable(L, 0, 5); lua_createtable(L, 0, 5);
set_field<lua_get_text_cursor>(L, "get_cursor"); set_field<lua_get_text_cursor>(L, "get_cursor");
set_field<lua_set_text_cursor>(L, "set_cursor"); set_field<lua_set_text_cursor>(L, "set_cursor");

View file

@ -40,6 +40,7 @@
#include "time_range.h" #include "time_range.h"
#include "async_video_provider.h" #include "async_video_provider.h"
#include "utils.h" #include "utils.h"
#include "video_frame.h"
#include <libaegisub/ass/time.h> #include <libaegisub/ass/time.h>
@ -222,6 +223,11 @@ int VideoController::FrameAtTime(int time, agi::vfr::Time type) const {
return context->project->Timecodes().FrameAtTime(time, type); return context->project->Timecodes().FrameAtTime(time, type);
} }
std::shared_ptr<VideoFrame> VideoController::GetFrame(int frame, bool raw) const {
double timestamp = TimeAtFrame(frame, agi::vfr::EXACT);
return provider->GetFrame(frame, timestamp, raw);
}
void VideoController::OnVideoError(VideoProviderErrorEvent const& err) { void VideoController::OnVideoError(VideoProviderErrorEvent const& err) {
wxLogError( wxLogError(
"Failed seeking video. The video file may be corrupt or incomplete.\n" "Failed seeking video. The video file may be corrupt or incomplete.\n"

View file

@ -39,6 +39,7 @@ class AssDialogue;
class AsyncVideoProvider; class AsyncVideoProvider;
struct SubtitlesProviderErrorEvent; struct SubtitlesProviderErrorEvent;
struct VideoProviderErrorEvent; struct VideoProviderErrorEvent;
struct VideoFrame;
namespace agi { namespace agi {
struct Context; struct Context;
@ -159,4 +160,5 @@ public:
int TimeAtFrame(int frame, agi::vfr::Time type = agi::vfr::EXACT) const; int TimeAtFrame(int frame, agi::vfr::Time type = agi::vfr::EXACT) const;
int FrameAtTime(int time, agi::vfr::Time type = agi::vfr::EXACT) const; int FrameAtTime(int time, agi::vfr::Time type = agi::vfr::EXACT) const;
std::shared_ptr<VideoFrame> GetFrame(int frame, bool raw) const;
}; };