Skip to content

Commit

Permalink
CI-Build 2022-08-07
Browse files Browse the repository at this point in the history
  • Loading branch information
arch1t3cht committed Aug 8, 2022
2 parents 4e8c02d + 17d50d4 commit 3127167
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 2 deletions.
69 changes: 69 additions & 0 deletions automation/v4-docs/get-frame.txt
Original file line number Diff line number Diff line change
@@ -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&"

---
9 changes: 9 additions & 0 deletions automation/v4-docs/gui.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,12 @@ Returns: 0 values

---

Determining whether there are unsaved changes

function aegisub.gui.is_modified()

Returns: 1 boolean
1. Whether the current file has unsaved changes.

---

123 changes: 121 additions & 2 deletions src/auto4_lua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@
#include "project.h"
#include "selection_controller.h"
#include "subs_controller.h"
#include "video_controller.h"
#include "text_selection_controller.h"
#include "video_controller.h"
#include "video_frame.h"
#include "utils.h"

#include <libaegisub/dispatch.h>
Expand Down Expand Up @@ -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)
{
if (const agi::Context *c = get_context(L))
Expand Down Expand Up @@ -319,6 +430,12 @@ namespace {
return 0;
}

int lua_is_modified(lua_State *L)
{
push_value(L, get_context(L)->subsController->IsModified());
return 1;
}

int project_properties(lua_State *L)
{
const agi::Context *c = get_context(L);
Expand Down Expand Up @@ -523,11 +640,13 @@ namespace {
set_field<project_properties>(L, "project_properties");
set_field<lua_get_audio_selection>(L, "get_audio_selection");
set_field<lua_set_status_text>(L, "set_status_text");
lua_createtable(L, 0, 4);
set_field<get_frame>(L, "get_frame");
lua_createtable(L, 0, 5);
set_field<lua_get_text_cursor>(L, "get_cursor");
set_field<lua_set_text_cursor>(L, "set_cursor");
set_field<lua_get_text_selection>(L, "get_selection");
set_field<lua_set_text_selection>(L, "set_selection");
set_field<lua_is_modified>(L, "is_modified");
lua_setfield(L, -2, "gui");

// store aegisub table to globals
Expand Down
6 changes: 6 additions & 0 deletions src/video_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "time_range.h"
#include "async_video_provider.h"
#include "utils.h"
#include "video_frame.h"

#include <libaegisub/ass/time.h>

Expand Down Expand Up @@ -222,6 +223,11 @@ int VideoController::FrameAtTime(int time, agi::vfr::Time type) const {
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) {
wxLogError(
"Failed seeking video. The video file may be corrupt or incomplete.\n"
Expand Down
2 changes: 2 additions & 0 deletions src/video_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class AssDialogue;
class AsyncVideoProvider;
struct SubtitlesProviderErrorEvent;
struct VideoProviderErrorEvent;
struct VideoFrame;

namespace agi {
struct Context;
Expand Down Expand Up @@ -159,4 +160,5 @@ class VideoController final : public wxEvtHandler {

int TimeAtFrame(int frame, 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;
};

0 comments on commit 3127167

Please sign in to comment.