Skip to content
This repository has been archived by the owner on May 12, 2024. It is now read-only.

Add automation function to get frame #53

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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&"

---
112 changes: 112 additions & 0 deletions src/auto4_lua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "selection_controller.h"
#include "subs_controller.h"
#include "video_controller.h"
#include "video_frame.h"
#include "utils.h"

#include <libaegisub/dispatch.h>
Expand Down Expand Up @@ -199,6 +200,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 @@ -489,6 +600,7 @@ 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");
set_field<get_frame>(L, "get_frame");

// store aegisub table to globals
lua_settable(L, LUA_GLOBALSINDEX);
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;
};