LoginSignup
y_emon
@y_emon (江門 Y)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

PHP x ffmpegで動画をエンコードしサムネイルを生成すると、WEB上にサムネイルが潰れて表示される

PHP x ffmpegで動画をエンコードしサムネイルを生成すると、WEB上にサムネイルが潰れて表示される。

【前提】

・PHPでffmpegを用いてエンコードを行っている。
・エンコード時 ffmpeg_4.0.2使用

-スマホ(iphone)のインカメで撮影時、アスペクト比率は縦の場合も横の場合でも同じである。
1920×1080 / 1920×1080
ゆえに、縦撮りの動画をそのままアップロードすると横に表示される。
ffmpeg_4.0.2を使うことでスマホで撮った縦撮り動画は適切に縦で表示される。
サムネイル生成をffmpeg_4.0.2で行ったがサムネイルが潰れて表示されるままだった。

【実現したいこと】

動画と同じアスペクト比(9:16)を保ち、サムネイルを生成したい。
なお、余白部分には黒帯を適用。

【以下コード】

//エンコード部分
$do = $ENV . "./ffmpeg_4.0.2 -i {$movie}/{$fn} -vcodec libx264 -g 0 -qcomp 1.0 -qmin 8 -qmax 32 -qdiff 4 -subq 6 -me_range 16 -i_qfactor 0.714286 -r 60 -s " . $newwidth ."x" . $newheight . " -crf 20 -ac 2 -ar 44100  {$movie}/{$fn_}.mp4";    


~~~中略~~~


// ffmpegを使って、動画の情報を取得
exec("./ffmpeg -i {$movie}/{$fn} 2>&1", $output);


~~~中略~~~

//サムネ生成部分
$do = $ENV . "./ffmpeg -y -vframes 1 -ss 2 -i {$movie}/{$fn_}.mp4 -f image2 {$movie}/{$fn_}_1.jpg";
echo `{$do}`;
_log($do);






実際にやってみたこと
以下のようにサイズを指定してみるも全く動きませんでした

//サムネ生成部分
$do = $ENV . "./ffmpeg -y -vframes 1 -ss 2 -s [サイズ指定o x o] -i {$movie}/{$fn_}.mp4 -f image2 {$movie}/{$fn_}_1.jpg";
echo `{$do}`;

参考画像

このようにサムネイルが表示されるのを

00000300_2.jpg

↓このように表示させたい
スクリーンショット (14).png

0

1Answer

初学者なため誤りなどあるかもしれません。
また環境やバージョン違いによって一部挙動の違いがあるやもしれませんがご容赦ください。

環境

  • OS: Debian GNU/Linux 10 (buster)
  • ffmpeg: version 4.1.6-1~deb10u1

前段

iPhoneで撮影した動画をMP4形式に変換し、サムネイルを作成することを要件とします。
PHPで実装されているようですがexecで外部コマンドを実行しているだけのようなのでコマンドラインからffmpegを実行して検証しました。
まずはiPhoneを縦向き、横向きの状態で動画を撮影してtate.MOVとyoko.MOVを作成しています。

オリジナルの動画ファイルからffprobeで情報を取得してみる。

$ ffprobe -v quiet -show_streams -print_format json tate.MOV | jq -r '.streams[0].width,.streams[0].height,.streams[0].tags.rotate'
1920
1080
90

$ ffprobe -v quiet -show_streams -print_format json yoko.MOV | jq -r '.streams[0].width,.streams[0].height,.streams[0].tags.rotate'
1920
1080
null

それぞれ以下の結果になりました。
縦向き動画
width:1920, height:1080, rotate:90

横向き動画
width:1920, height:1080, rotate:null

最小限のパラメータでMP4に変換してみる

-s オプションでサイズ指定で変換してみます。
16:9のアスペクト比を維持して変換なので1920x10801280x720に変換します。

縦向き動画の変換

$ ffmpeg -y -v quiet -i tate.MOV -pix_fmt yuv420p -s 1280x720 tate.mp4

結果は縦方向に潰れた動画になってしまいました。
なぜならば、90度の回転(rotate)を無視してサイズを指定してしまっているからです。
この場合はwidth:1080, height:1920の動画として処理する必要があります。
よって正しくは以下のように指定します。

$ ffmpeg -y -v quiet -i tate.MOV -pix_fmt yuv420p -s 720x1280 tate.mp4

横向き動画の変換

$ ffmpeg -y -v quiet -i yoko.MOV -pix_fmt yuv420p -s 1280x720 yoko.mp4

横向き動画は特に問題ありませんでした。

サムネイルを作成する

まずはサイズなどを指定しないで作成してみます。

$ ffmpeg -y -v quiet -i tate.mp4 -ss 2 -vframes 1 -f image2 tate_1.jpg
$ ffmpeg -y -v quiet -i yoko.mp4 -ss 2 -vframes 1 -f image2 yoko_1.jpg

1フレーム抜き出して画像にしているので当然ではありますが、以下のサイズの画像ができました。
tate_1.jpg: width:720, height:1280
yoko_1.jpg: width:1280, height:720

次にアスペクト比を維持したまま、1280x720に収まるサムネイルを作成してみます。
長辺である縦の1280pxが720pxに収まるように変換します。
リサイズには-vfオプションを使用します。短辺側は-1を指定することでアスペクト比を維持した適切な値が自動で設定されます。

$ ffmpeg -y -v quiet -i tate.mp4 -ss 2 -vframes 1 -vf scale="-1:720" -f image2 tate_2.jpg

tate_2.jpg



そろそろ完成が見えてきました。
次に帯を付与したサムネイルを作成します。
帯をつけるには-vfオプションでpadを設定します。
padの書式はwidth:height:左右のpixel数:上下のpixel数:帯の色となります。

$ ffmpeg -y -v quiet -i tate.mp4 -ss 2 -vframes 1 -vf scale="-1:720,pad=1280:720:(ow-iw)/2:(oh-ih)/2:black" -f image2 tate_3.jpg

padで使用している評価式のそれぞれの意味
ow = 出力する横幅
iw = 入力の横幅
oh = 出力する縦幅
ih = 入力の縦幅

tate_3.jpg

以上の手順で帯のあるサムネイル画像を作成することができました。

問題解決に向けて、以下も試して見てください。
1. 最小限のパラメータを指定する
2. 変数を固定値にするなどデバッグしやすいシンプルな作りにする
3. サムネイル生成前のエンコードした動画が潰れていないか確認する
4. PHPからffmpegを実行するのではなくコマンドラインで直接操作して確認する

以上、よろしくおねがいします。

1

Comments

  1. @y_emon

    Questioner
    ご回答ありがとうございました。
    おっしゃる通りコマンドラインでは正常に処理できておりました。

    しかし、実用しているウェブサイトの機能では
    画像がただ横になるという結果でした。

    どうやら動画エンコードの時点で
    パラメータ等修正する必要がありそうですね。

    -vframes 1 を入力ファイルに指定すると
    コマンドラインではエラーになることから、
    現在はバグで奇跡的に動いているように伺えます。

    その他のPHPのコードが起因している可能性も含め
    検討しながら試行錯誤してみます。ありがとうございました。

Your answer might help someone💌