laravel-ffmpegを使って動画の変換をしてみる

# はじめに

[ffmpeg](https://ffmpeg.org)は、動画と音声を記録・変換・再生するためのソフトです。
phpでffmpegを使用できる[PHP-FFMpeg](https://github.com/PHP-FFMpeg/PHP-FFMpeg)をラッパーしたのが[laravel-ffmpeg](https://github.com/pascalbaljetmedia/laravel-ffmpeg)です。
laravel-ffmpegを使うにはffmpeg自体も必要なので、事前にインストールしておきます。

# インストール

※ ffmpegはインストールされている前提で進めます。

laravelプロジェクトのルートディレクトリで
```composer require pbmedia/laravel-ffmpeg
```を実行します。

composer.jsonとcomposer.lockにlaravel-ffmpegが追記されればOK。

# config/app.phpの修正

以下を追記します。

```php:app.php
'providers' => [
    ...
    Pbmedia\LaravelFFMpeg\FFMpegServiceProvider::class,
    ...
];

'aliases' => [
    ...
    'FFMpeg' => Pbmedia\LaravelFFMpeg\FFMpegFacade::class
    ...
];
```

# artisanコマンド実行

laravelプロジェクトのルートディレクトリで
`php artisan vendor:publish --provider="Pbmedia\LaravelFFMpeg\FFMpegServiceProvider"`
を実行します。

configディレクトリ配下にlaravel-ffmpeg.phpが作成されていることを確認します。

```php:laravel-ffmpeg.php
<?php

return [
    'default_disk' => 'local',

    'ffmpeg' => [
        'binaries' => env('FFMPEG_BINARIES', 'ffmpeg'),
        'threads' => 12,
    ],

    'ffprobe' => [
        'binaries' => env('FFPROBE_BINARIES', 'ffprobe'),
    ],

    'timeout' => 3600,
];
```

# ffmpegとffprobeのパスを設定

以下のコマンドを実行してffmpegとffprobeのパスを確認します。

`which ffmpeg` >> /usr/bin/ffmpeg
`which ffprobe` >> usr/bin/ffprobe

このパスをlaravel-ffmpeg.phpか.envに追記します。

### laravel-ffmpeg.phpを修正する場合

```php:laravel-ffmpeg.php
<?php

return [
    'default_disk' => 'local',

    'ffmpeg' => [
        'binaries' => env('FFMPEG_BINARIES', '/usr/bin/ffmpeg'),
        'threads' => 12,
    ],

    'ffprobe' => [
        'binaries' => env('FFPROBE_BINARIES', '/usr/bin/ffprobe'),
    ],

    'timeout' => 3600,
];
```

修正後は、`php artisan config:clear`でキャッシュクリアしておきます。

### .envを修正する場合

```:.env
FFMPEG_BINARIES=/usr/bin/ffmpeg
FFPROBE_BINARIES=/usr/bin/ffprobe
```

修正後は、`php artisan cache:clear`でキャッシュクリアしておきます。

これで準備完了！

# 変換してみる

storage/app/public/配下のsample.mp4の動画の長さとコーデック名を取得し、変換前後の解像度とビットレートを画面に表示してみます。

laravel authで作成した素のHomeControllerを修正して/homeにアクセスした時に処理を実行します。

```php:HomeController
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use FFMpeg;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
        // 変換前のファイル取得
        $media = FFMpeg::fromDisk('public')->open('sample.mp4');
        $mediaStreams = $media->getStreams()->first();

        // 再生時間を取得
        $durationInSeconds = $media->getDurationInSeconds();

        // コーデックを取得
        $codec = $mediaStreams->get('codec_name');

        // 解像度(縦)を取得
        $height = $mediaStreams->get('height');

        // 解像度(横)を取得
        $width = $mediaStreams->get('width');

        // ビットレートを取得
        $bit_rate = $mediaStreams->get('bit_rate');

        // SD画質に変換
        $media->addFilter(function ($filters) {
            $filters->resize(new \FFMpeg\Coordinate\Dimension(720, 480));
        })
        ->export()
        ->toDisk('public')
        ->inFormat(new \FFMpeg\Format\Video\X264('aac'))
        ->save('sample_SD.mp4');

        // 変換後のファイル取得
        $mediaSD = FFMpeg::fromDisk('public')->open('sample_SD.mp4');
        $mediaStreamsSD = $mediaSD->getStreams()->first();

        // 解像度(縦)を取得
        $height_SD = $mediaStreamsSD->get('height');

        // 解像度(横)を取得
        $width_SD = $mediaStreamsSD->get('width');
        
        // ビットレートを取得
        $bit_rate_SD = $mediaStreamsSD->get('bit_rate');

        // Viewで確認
        return view('home')->with([
            "durationInSeconds" => $durationInSeconds,
            "codec" => $codec,
            "height" => $height,
            "width" => $width,
            "bit_rate" => $bit_rate,
            "height_SD" => $height_SD,
            "width_SD" => $width_SD,
            "bit_rate_SD" => $bit_rate_SD,
        ]);
    }
}
```

```php:home.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Dashboard</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif
                    動画の再生時間は{{$durationInSeconds}}秒です。<br>
                    コーデックは{{$codec}}です。<br>
                    変換前は解像度{{$width}}×{{$height}}でビットレートは{{$bit_rate}}bpsです。<br>
                    変換後は解像度{{$width_SD}}×{{$height_SD}}でビットレートは{{$bit_rate_SD}}bpsです。<br>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
```

実行結果はこんな感じ。
<img width="545" alt="スクリーンショット 2019-08-31 20.00.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263328/b427d333-14cb-9a99-f23c-e2f63ac24127.png">

# ビットレートを指定して変換してみる

変換の処理を以下のように書き換えて500kbpsで変換してみます。

```php:HomeController
        // SD画質に変換
        $video = new \FFMpeg\Format\Video\X264('aac');
        $video->setKiloBitrate(500);
        $media->addFilter(function ($filters) {
            $filters->resize(new \FFMpeg\Coordinate\Dimension(720, 480));
        })
        ->export()
        ->toDisk('public')
        ->inFormat($video)
        ->save('sample_SD.mp4');
```

実行結果はこんな感じ。
<img width="555" alt="スクリーンショット 2019-08-31 20.20.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263328/b70b5750-7d8c-8f0c-8ba1-13cc6114161b.png">

フレームレートの指定もできるようなので、色々試してみたいと思います！

# 2020/5/10追記

addFilterを使うとコマンドと同じように値を指定できることを発見したので追記です。
こんな感じ

```
->addFilter('-vf', config('scale=320:180,pad=y=(ih-oh)/2:aspect=4/3'))
->addFilter('-r', config('15'))
->addFilter('-level', config('2'))
->addFilter('-ar', config('8000'))
```