protonemedia / laravel-ffmpeg

This package provides an integration with FFmpeg for Laravel. Laravel's Filesystem handles the storage of the files.
https://protone.media/en/blog/how-to-use-ffmpeg-in-your-laravel-projects
MIT License
1.63k stars 194 forks source link

Audio Endcoding to HLS when cover artwork is present #452

Open Orrison opened 1 year ago

Orrison commented 1 year ago

I am running a process using this package to convert uploaded audio files to an HLS format. It is working great for the vast majority of media. But it seems that it errors out for some audio files. It seems this happens when the audio file has cover artwork within it.

Is there a way to ignore the cover artwork during the conversion?

$kiloBitrate = (int) round(
                FFMpeg::fromDisk('s3')
                    ->open($originalFilePath)
                    ->getAudioStream()
                    ->get('bit_rate') / 1000
            );

            $export = FFMpeg::fromDisk('s3')
                ->open($originalFilePath)
                ->exportForHLS();

            if ($kiloBitrate < 128) {
                $export->addFormat((new X264())->setAudioKiloBitrate($kiloBitrate)->setKiloBitrate($kiloBitrate));
            }

            if ($kiloBitrate >= 128) {
                $export->addFormat((new X264())->setAudioKiloBitrate(128)->setKiloBitrate(128));
            }

            if ($kiloBitrate > 128 && $kiloBitrate < 320) {
                $export->addFormat((new X264())->setAudioKiloBitrate($kiloBitrate)->setKiloBitrate($kiloBitrate));
            }

            if ($kiloBitrate >= 320) {
                $export->addFormat((new X264())->setAudioKiloBitrate(320)->setKiloBitrate(320));
            }

            if ($kiloBitrate > 320 && $kiloBitrate < 1411) {
                $export->addFormat((new X264())->setAudioKiloBitrate($kiloBitrate)->setKiloBitrate($kiloBitrate));
            }

            if ($kiloBitrate >= 1411) {
                $export->addFormat((new X264())->setAudioKiloBitrate(1411)->setKiloBitrate(1411));
            }

            $export->toDisk('s3')
                ->save($saveToFilePath)
                ->cleanupTemporaryFiles();
[mp3 @ 0x5623e624da00] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from '/tmp/2abe657c43447927//media/189/song.mp3':
  Metadata:
    track           : 1
    album           : The album
    encoded_by      : Fraunhofer IIS MP3 v04.01.02 (fast)
    artist          : The artist
    title           : Song
    date            : 2015
  Duration: 00:03:42.00, start: 0.000000, bitrate: 139 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 128 kb/s
    Stream #0:1: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 1140x985 [SAR 144:144 DAR 228:197], 90k tbr, 90k tbn, 90k tbc (attached pic)
    Metadata:
      comment         : Other
Stream mapping:
  Stream #0:0 -> #0:0 (mp3 (mp3float) -> aac (native))
  Stream #0:1 -> #0:1 (mjpeg (native) -> h264 (libx264))
Press [q] to stop, [?] for help
[hls @ 0x5623e62521c0] Frame rate very high for a muxer not efficiently supporting it.
Please consider specifying a lower framerate, a different muxer or -vsync 2
[libx264 @ 0x5623e6260240] height not divisible by 2 (1140x985)
Error initializing output stream 0:1 -- Error while opening encoder for output stream #0:1 - maybe incorrect parameters such as bit_rate, rate, width or height
Conversion failed!
magic-thomas commented 1 year ago

me here too.

Orrison commented 1 year ago

me here too.

I got around this by finding out whether or not there were image streams in the item and stripping them:


$originalFilePath = $this->media->getPath();

$directory = pathinfo($originalFilePath, PATHINFO_DIRNAME) . '/conversions/';

$saveToFilePath = $directory . pathinfo($originalFilePath, PATHINFO_FILENAME) . '.m3u8';

$openedFile = FFMpeg::fromDisk('s3')
    ->open($originalFilePath);

$coverArtStreams = collect($openedFile->getStreams())->filter(function (Stream $stream) {
    return $stream->isVideo() && in_array($stream->get('codec_name'), ['png', 'jpeg', 'mjpeg']);
});

// If there are cover art streams, we need to strip them out
if ($coverArtStreams->isNotEmpty()) {
    $audioOnlyFilePath = $directory . pathinfo($originalFilePath, PATHINFO_FILENAME) . '-audio-only.mp3';

    $audioOnlyFile = $this->convertToAudioOnly(
        $openedFile,
        $audioOnlyFilePath
    );

    $this->convertToHls($audioOnlyFile, $audioOnlyFilePath, $saveToFilePath);

    Storage::disk('s3')->delete($audioOnlyFilePath);

    return $saveToFilePath;
}

$this->convertToHls($openedFile, $originalFilePath, $saveToFilePath);

return $saveToFilePath;

protected function convertToAudioOnly(MediaOpener $openedFile, string $directory): MediaOpener
    {
        Log::info('Cover art stream found, stripping it to prevent encoding errors', [
            'media_id' => $this->media->id,
        ]);

        return $openedFile
            ->addFilter(['-vn'])
            ->export()
            ->toDisk('s3')
            ->inFormat(new Mp3())
            ->save($directory);
    }
hardevine commented 1 month ago

any update on this ?, can we add a helper method in the package for this ?