この記事は、Kyoto University Advent Calendar 2021の25日目の記事です。まだ25日だからセーフです1。こんなに忙しくなるとは思ってなかったんだ……。後日もっと丁寧なのに差し替えます……。
概要
画像を一定周期で拡大縮小させるOBSプラグインを作成しました。
目的
去年作ったアプリを友人に見せたらもっといろいろできるはずと言われたのでOBSのフィルタという形で実装すれば楽しいのではと思い作ることにしました。
とりあえず簡単な気がしたので心拍に合わせてサイズが変わるフィルタの実装を目指します。通信部が間に合わなかったので今回は固定周波数で実装しました。
方法
OBSの開発環境を用意する。
OBSのサイトのBuild from sourceを見ながら環境を整えていきます。ハマりどころがいくつかありますがこのブログがわかりやすかったです。
プラグインのプロジェクトを作る
プラグインに関する公式ドキュメントを参考にCmakeListを書いてプロジェクトを作ります。僕はそんな余裕がなかったのでサンプルプロジェクトを上書きしました。
コードを書く
プラグインに関する公式ドキュメントのみを参考に書くのはかなり厳しいので類似するプラグインを探しました。
するとOBS Scale To Soundという音で画像サイズを変化させるプラグインが見つかったのでこれを見て書く事にしました。詳細な中身については割愛して(後で追加しますごめんなさい)、参考プログラムからの大きな変更点のみをまとめます。
このプラグインは元画像のサイズを音量に比例したサイズになるように変更しており、それはobs_source_info
構造体のvideo_render
に渡す関数の中で行われています。フィルタエフェクトを触る時は直前にobs_enter_graphics
とobs_source_process_filter_begin
を呼び出し、終了時にobs_source_process_filter_end
とobs_leave_graphics'を呼びます。実はフィルタから出力される画像のサイズは
obs_source_process_filter_end`に渡す引数で決まります。なので引数にいい感じにスケーリングしたサイズ情報を渡せば画像を拡大縮小させることができます。ただそのまま拡大縮小させると、画像の左上を基準にスケールされてしまうのでスケールすると同時に画像を斜め方向に移動させて中心をそろえる必要があります。拡大縮小と移動を同時に行うコードはすでに参考プログラムが書いてくれているのでこれを一定周波数の正弦波によってスケーリングされるように書き換えれば目的は達成です。
僕が書いたvideo_render
に渡す関数は以下の通りです。
static void hrv_filter_render(void *data, gs_effect_t *effect)
{
int16_t pchRequest[BUFSIZE];
DWORD cbRead;
struct hrv_filter_data *filter = data;
int32_t w = filter->src_w;
uint32_t h = filter->src_h;
DWORD now = GetTickCount();
float duration = (now - filter->last_time) / 1000.0f;
float s = filter->hear_rate * duration / 60;
float _sin = sinf(2 * M_PI * s);
if (s > 1) {
filter->last_time =
now - (DWORD)(duration - 60 / filter->hear_rate);
}
filter->current_scale = fmax(_sin * filter->scale + 1, 0);
obs_enter_graphics();
if (!obs_source_process_filter_begin(filter->context, GS_RGBA,
OBS_ALLOW_DIRECT_RENDERING))
return;
gs_effect_t *move_effect = filter->effect;
gs_eparam_t *move_val =
gs_effect_get_param_by_name(move_effect, "inputPos");
gs_eparam_t *show = gs_effect_get_param_by_name(move_effect, "show");
gs_effect_set_float(show, 1.0f);
/*if (audio_w <= 0 || audio_h <= 0) {
gs_effect_set_float(show, 0.0f);
audio_w = 1;
audio_h = 1;
}*/
//Change the position everytime so it looks like it's scaling from the center
struct vec4 move_vec;
vec4_set(&move_vec, (w - w * filter->current_scale) / 2, (h - h * filter->current_scale) / 2,
0.0f, 0.0f);
gs_effect_set_vec4(move_val, &move_vec);
obs_source_process_filter_end(filter->context, move_effect,
w * filter->current_scale,
h * filter->current_scale);
obs_leave_graphics();
blog(LOG_DEBUG, "w%ld, h%ld, scale%f", w * filter->current_scale,
h * filter->current_scale,
filter->current_scale);
UNUSED_PARAMETER(effect);
}
結果
こんな感じ。周期は60Hz固定です。Scaleを変えると振幅が変わります。
今後の展望
今回は心拍センサとの連携が全く持って間に合わずとりあえずフィルタだけ書いた形となりました。早急に心拍センサと連携したい所存です。去年作ったアプリが心拍数をブロードキャストしてプラグインが受信する形にしたいなと考えています。
-
投稿ボタンを押す瞬間までは25日でしたが、タグが指定されていないというエラーで差し戻されて26日になりました。 ↩