PHP
ffmpeg
ffmpegDay 3

スマホで撮影した映像をffmpegで正しい向きに変換する(+縮小変換)

More than 3 years have passed since last update.

スマホで撮影した映像は、デバイスの回転情報が入っており、PCなど端末によっては正常ではない向きで映像が再生される場合があります。

そこでffmpegを使い、映像の回転情報を取得し、映像に回転変換をかけて正常に再生されるようにしてみます。


ビデオ情報の取得

ffmpegパッケージに同梱されているffprobeプログラムを使うと映像情報を取得することができます。

ffprobe ビデオファイル名

しかしこのままではプログラムから使いにくいのでJSONフォーマットで出力します。さらに標準エラー出力に設定情報が出力されて余計なので捨てます。

ffprobe -show_streams -print_format json ビデオファイル名 2>/dev/null

出力例

{

"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_type": "video",
・・・
"width": 1920,
"height": 1080,
・・・
},
"tags": {
"rotate": "90",
・・・
}
}
]
}

回転情報はstreams[]->tags->rotate、幅はstreams[]->width、高さはstreams[]->heightに入ります。

streamsは配列になっていて、codec_typeが「video」の要素がビデオの情報です。

rotateが90というのは、左に90度回転した映像であるということなので、ffmpegによって右に90度回転させ、さらに回転情報を削除すれば、どんな端末でも正常な向きで再生されるわけです。


回転変換をかけて出力する


rotateが90の場合

ffmpeg -i 入力ファイル名 -vf transpose=1 -metadata:s:v:0 rotate=0 出力ファイル名

-vf transpose=1で映像を右に90度回転します。

-metadata:s:v:0 rotate=0で元の回転情報を削除します。


rotateが270の場合

ffmpeg -i 入力ファイル名 -vf transpose=2 -metadata:s:v:0 rotate=0 出力ファイル名

-vf transpose=1で映像を左に90度回転します。


rotateが180の場合

ffmpeg -i 入力ファイル名 -vf hflip,vflip -metadata:s:v:0 rotate=0 出力ファイル名

-vf hflip,vflipで映像を左右に反転し、さらに上下に反転しています。


映像変換プログラムのサンプル

PHPで変換サンプルを作りました。ffmpegとffprobeにパスが通っていれば動くと思います。

使い方はコメントを参照してください。


convert_movie.php

<?php

// ムービー変換
// php convert_movie.php infile outfile (long_side)
// long_sideは長辺の長さ。省略時は640。

// 引数チェック
if (count($argv) != 3 && count($argv) != 4) {
echo "引数の数が違います。\n";
echo "使い方: php convert_movie.php infile outfile (long_side)\n";
exit(1);
}

$infile = $argv[1];
$outfile = $argv[2];
if (isset($argv[3])) $long_side = $argv[3];
else $long_side = 640;

// 入力ファイルチェック
exec("ffprobe -show_streams -print_format json $infile 2>/dev/null", $out, $rc);
if ($rc != 0) {
echo "入力ファイルエラー。\n";
exit(1);
}

$json = implode("\n", $out);
$info = json_decode($json);
$stream = null;
foreach ($info->streams as $s) {
if ($s->codec_type == 'video') {
$stream = $s;
break;
}
}

if (!$stream) {
echo "動画情報が見つかりません。\n";
exit(1);
}

// 入力ファイルの幅・高さ・回転角度を取得する。
if (!$stream->width) {
echo "入力ファイルの幅が取得できません。\n";
exit(1);
}
$width = $stream->width;

if (!$stream->height) {
echo "入力ファイルの高さが取得できません。\n";
exit(1);
}
$height = $stream->height;

if (isset($stream->tags->rotate)) $rotate = $stream->tags->rotate;
else $rotate = '0';

// 回転角度を元に出力ファイルを回転する。
switch ($rotate) {
case '90':
$vf = '-vf transpose=1';
$swapwh = true;
break;
case '180':
$vf = '-vf hflip,vflip';
$swapwh = false;
break;
case '270':
$vf = '-vf transpose=2';
$swapwh = true;
break;
default:
$vf = '';
$swapwh = false;
}

echo "入力ファイル 幅:$width 高さ:$height 回転角度:$rotate\n";

// 長辺を元に縮小する。
// 指定された長辺より短い場合はそのまま。
if ($width > $height) {
if ($width > $long_side) {
$height = (int)($height * $long_side / $width);
$width = $long_side;
}
} else {
if ($height > $long_side) {
$width = (int)($width * $long_side / $height);
$height = $long_side;
}
}

// 回転角度が90度・270度のときは幅と高さを逆転する。
if ($swapwh) {
$size = $height . 'x' . $width;
} else {
$size = $width . 'x' . $height;
}

exec("ffmpeg -y -i $infile $vf -vcodec libx264 -metadata:s:v:0 rotate=0 -s $size $outfile 2>&1", $out, $rc);
if ($rc != 0) {
echo "動画変換エラー。\n";
exit(1);
}

exit(0);