新年も明けて2020年になりましたね!
話は飛びますが、年末辺りからTwitterのエンジニア界隈でルパン三世を模した動画を幾度と無くTLで見ました!
ググって同様の動画を作成するジェネレーターを幾つか確認しましたが、せっかくならPHPで動画作成したい!
ということで開発しました!
※都度、リファクタを加えながら開発してたので5時間くらいかかりましたw
※出力結果は音付きのmp4ファイルです
この記事で解説をするコードの立ち回りは、PHPの言語仕様の理解を深める特殊な立ち回りに詳しく書きました!
環境
動作確認済み環境
- macOS Catalina :
ver 10.15.2
- PHP :
7.3.1
- Web Server :
Apache
必要な外部ライブラリ
その他特記事項
- PHPにGDモジュールがインストールされていること
実行方法
FFmpegのコマンドパスが通った上記の環境下で、
lupin-generator/index.php
の
$string = "あけおめ!";
を編集して実行してください!
出力先は、
lupin-generator/output/output.mp4
です。
※既に出力結果が存在する場合は、上書き保存されます
設定関連
基本的に画像やフレームの作成に用いるパラメータは、
lupin-generator/Define.php
に記述しています。
以下は各定数の概要です。
定数 | 概要 |
---|---|
L_FFMPEG_COMMAND_PATH | FFmpegのコマンドパス |
L_FONT_FILE | 出力する文字のフォント(TrueType)ファイルパス。デフォルトではGoogleのWebフォントを利用 |
L_IMG_WIDTH | 出力結果の横幅 |
L_IMG_HEIGHT | 出力結果の縦幅 |
L_BACKGROUND_COLOR_R | 出力結果背景のR(0 〜 255) |
L_BACKGROUND_COLOR_G | 出力結果背景のG(0 〜 255) |
L_BACKGROUND_COLOR_B | 出力結果背景のB(0 〜 255) |
L_FONT_COLOR_R | 出力結果文字のR(0 〜 255) |
L_FONT_COLOR_G | 出力結果文字のG(0 〜 255) |
L_FONT_COLOR_B | 出力結果文字のB(0 〜 255) |
L_FONT_SIZE | 1文字専用フレームのフォントサイズ |
L_IMG_ANGLE | 文字の角度 |
L_IMG_X | 左上を基点としたX軸方向の開始位置 |
L_IMG_Y | 左上を基点としたY軸方向の開始位置 |
L_TEMP_IMAGE_PATH | フレームの作成に必要な画像ファイルの保存パス |
L_TEMP_FRAME_PATH | 動画の作成に必要なフレームファイルの保存パス |
L_OUTPUT_PATH | 出力結果(動画)の保存パス |
L_TYPE_SOUND | タイピングサウンドの参照パス |
L_TITLE_SOUND | タイトルサウンドの参照パス |
L_FRAME_LIST_FILE | FFmpegを用いてフレームを連結する際に参照パスリストを記述するファイルのパス |
ギジュツ的なこと
内部仕様
- 入力文字列を1文字ずつ画像に書き出す
- 入力文字列をタイトル画像に書き出す
- 画像を1枚ずつサウンド付きのフレームに書き出す
- フレームを連結する
その他
コマンドラインから引数を投げて実行することを想定しても良かったのですが、それならそもそもPHPで書かないので直ガキ仕様にしました笑
あと、出力結果は一行で文字列を埋めるので、
\n
区切りでパースしたり、形態素解析のIgo等用いて違和感無く複数行にすることも出来たのですがやってません🥺
他に特別なことと言えば、
- リファクタリングに専念した
くらいです😢
個人でPHPのフレームワークを現在進行系で開発しているのですが、その際にCodeIgniterの本体を読み漁って覚えた立ち回り等は使ってます。
例えば、このルパン三世タイトルジェネレーターでは、
file_exists(L_FRAME_LIST_FILE) OR touch(L_FRAME_LIST_FILE);
といった処理があります。
大抵どの言語でも同様の仕様だと思いますが、論理和は左側の条件式の評価が
true
であれば、右側の条件式を評価せずに返り値を
true
とします。
上記コードの場合は
L_FRAME_LIST_FILE
に該当ファイルが存在していれば、左側の
file_exists(L_FRAME_LIST_FILE)
はtrueとなり右側の
touch(L_FRAME_LIST_FILE)
は実行されません。
逆にファイルが存在しなければ、右側の式も評価されることになるので実行されます。
要するに、
if ( !file_exists(L_FRAME_LIST_FILE) )
{
touch(L_FRAME_LIST_FILE)
}
と同様です。
あとは、マルチバイト文字も許容することになるので、それらの文字列を一文字ずつ配列に格納する処理を施しました!
$this->strings = array_values(
array_filter(
preg_split("//u",$this->string),
"strlen"
)
);
上記コードですが、まずは正規表を利用して入力文字列を配列に格納しています。
preg_split("//u",$this->string)
UTF-8にマッチする文字のみを検索パターンとしています。
"//u"
そして、上記処理を施すと空文字のvalueが格納されたインデックスが生成されるためにarray_filterで配列を走査しています。
array_filterの第二引数に指定するコールバック関数は返り値がfalseであるインデックスを削除した配列を返却します。
strlenは文字列の長さをint型で返却するPHPの組み込み関数で、上記のようなvalueが空文字のインデックスの場合は
0
を返却します。
これはPHPの公式マニュアルにある通り、int型の0をbool値として扱う場合は、
false
と評価されます。
そして、連番画像として扱っているため歯抜けのkey名をarray_valuesで整えて実装しています。
他と言えば実行演算子を覚えたくらいです😇
以上です!
ありがとうございました!