AE 全称 Adobe After Effects,是 adobe 公司推出的一款图形视频处理软件,其插件可以帮助我们更好地操作 AE。插件主要分为用户效果的和通用插件两种,入口函数也相应的分为 EffectMain() 和 EntryPointFunc() 两种,都是基于 c 或者 c++ 开发的。
这里我们仅讨论通用插件 AGEP 开发情况,入口函数如 demo 中提供的 Easy_Cheese 项目:
A_Err EntryPointFunc(
struct SPBasicSuite *pica_basicP, /* >> */
A_long major_versionL, /* >> */
A_long minor_versionL, /* >> */
AEGP_PluginID aegp_plugin_id, /* >> */
AEGP_GlobalRefcon *global_refconP) /* << */
{
S_my_id = aegp_plugin_id;
A_Err = A_Err_NONE, err2 = A_Err_NONE;
sP = pica_basicP;
AEGP_SuiteHandler suites(pica_basicP);
err = suites.CommandSuite1()->AEGP_GetUniqueCommand(&S_Easy_Cheese_cmd);
if (!err && S_Easy_Cheese_cmd) {
ERR(suites.CommandSuite1()->AEGP_InsertMenuCommand(S_Easy_Cheese_cmd, "Easy Cheese", AEGP_Menu_KF_ASSIST, AEGP_MENU_INSERT_SORTED));
}
ERR(suites.RegisterSuite5()->AEGP_RegisterCommandHook( S_my_id,
AEGP_HP_BeforeAE,
AEGP_Command_ALL,
CommandHook,
0));
ERR(suites.RegisterSuite5()->AEGP_RegisterUpdateMenuHook(S_my_id, UpdateMenuHook, 0));
ERR(suites.RegisterSuite5()->AEGP_RegisterIdleHook(S_my_id, IdleHook, 0));
if (err){ // not !err, err!
ERR2(suites.UtilitySuite3()->AEGP_ReportInfo(S_my_id, "Easy_Cheese : Could not register command hook."));
}
return err;
}
插件名称是通过方法 AEGP_InsertMenuCommand() 的第二个参数定义的,如果想改成自己的插件,可以定义为 "Test" 等。试着运行项目,会在如下目录生成对应包:
lucas@lucasdeMacBook-Pro Debug % pwd
/Users/lucas/Library/Developer/Xcode/DerivedData/Easy_Cheese-dymhdlixbjrvocgonjhlpbfqgqfk/Build/Products/Debug
lucas@lucasdeMacBook-Pro Debug % ll
total 0
drwxr-xr-x 3 lucas staff 96 10 7 16:29 Easy_Cheese.plugin
然后将其复制到 AE 插件安装目录下:
root@lucasdembp Plug-ins # pwd
/Applications/Adobe After Effects 2021/Plug-ins
root@lucasdembp Plug-ins # ll
total 0
drwxr-xr-x 3 root admin 96 8 16 14:25 (AdobePSL)
drwxr-xr-x 2 root admin 64 8 16 14:25 DataFormat
drwxr-xr-x 3 root admin 96 10 7 16:29 Easy_Cheese.plugin
drwxr-xr-x 216 root admin 6912 8 16 14:25 Effects
drwxr-xr-x 6 root admin 192 8 16 14:25 Extensions
drwxr-xr-x 15 root admin 480 8 16 14:25 Format
drwxr-xr-x 21 root admin 672 8 16 14:25 Keyframe
drwxr-xr-x 8 root admin 256 8 16 14:25 MAXON CINEWARE AE
重启 AE 后,在 AE 界面并没有看到 Test 插件,此处只需调整方法 AEGP_InsertMenuCommand() 的第三个参数,将其调整为 AEGP_Menu_WINDOW 就能在 "窗口" 菜单项看到对应插件了。
如果在入口函数中获取 layer 呢?
AEGP_LayerH layerPH;
ERR(suites.LayerSuite8()->AEGP_GetActiveLayer(&layerPH));
重新打包后再次打开 AE 时就直接 crash 了,这是因为再打开 AE 时还没有选定 layer,直接获取会导致应用程序奔溃。再看入口函数注意到还有个 AEGP_RegisterCommandHook() 方法,其中第四个参数 CommandHook 是一个钩子函数,试着在这里面定义看看呢?可以添加日志打印看下:
AEGP_LayerH layerH = NULL;
ERR(suites.LayerSuite8()->AEGP_GetActiveLayer(&layerH));
ERR(suites.LayerSuite8()->AEGP_GetLayerID(layerH, &layer_indexL));
writeLog("lay id:" + std::to_string(layer_indexL));
之后便在控制台中看到如下输出:
lay id:14
以上便是插件的一个大概框架,具体如何应用参考 🔗官方sdk文档 即可。如果本地想起一个第三方应用控制插件呢?可以通过套接字进行交互,先在插件中定义个服务端进行截断:
int main(int argc, char* argv[])
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
char recv_buf[1024];
struct sockaddr_in saddr, caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(22222);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sock, (struct sockaddr*)&saddr, sizeof(saddr));
listen(sock, SOMAXCONN);
socklen_t len = sizeof(caddr);
while (true) {
int clientsock = accept(sock, (struct sockaddr*)&caddr, &len);
memset(recv_buf, 0, 1024);
int recv_len = recv(clientsock, recv_buf, 1024, 0);
...
}
return 0;
}
重新打包后可看到 22222 端口已处于监听状态:
lucas@lucasdeMacBook-Pro Desktop % lsof -i:22222
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
After\x20 11767 lucas 319u IPv4 0xdd6a6034d90b 0t0 TCP *:22222 (LISTEN)
再定义个客户端:
int main()
{
string st = "hello world test";
char send_buf[200];
strcpy(send_buf, st.c_str());
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(22222);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(saddr);
connect(sock, (const struct sockaddr *)&saddr, len);
send(sock, send_buf, 200, 0);
}
将客户端传到服务端的数据也打印输出看看:
hello world test
lay id:14
至此就完成了第三方应用通过插件来控制AE的开发框架搭建了。
以上,欢迎交流~