m4b-tool icon indicating copy to clipboard operation
m4b-tool copied to clipboard

How to use FFmpeg filters (such as deesser)

Open csandman opened this issue 2 years ago • 3 comments

I was interested in adding a desser filter to my merge implementation, and I'm running into errors when I try it. It appears to be running properly during the initial conversion of each audio file, and failing on the concat command. So is there any way to apply filters during the initial conversion tasks, and not to the concat task? This would be a nice feature to add if not (perhaps a --ffmpeg-conversion-param argument).

This is the command I ran:

m4b-tool merge -vvv --no-interaction --fix-mime-type --ffmpeg-param="-filter_complex" --ffmpeg-param="deesser=i=.25" --output-file="output/final/Steph Cha/Steph Cha - Your House Will Pay.m4b" --logfile="config/logs/merge.log" --jobs=4 --name="Your House Will Pay" --album="Your House Will Pay" --artist="Steph Cha" --sortartist="Steph Cha, Greta Jung, Glenn Davis" --genre="Literature & Fiction/Genre Fiction" --writer="Greta Jung, Glenn Davis" --albumartist="Steph Cha" --year="2019" --copyright="©2019 Steph Cha (P)2019 HarperAudio" --cover="output/source/Steph Cha/Your House Will Pay/cover.jpg" --encoded-by="m4b-tool" "output/source/Steph Cha/Your House Will Pay"

And this is the output:

PHP Deprecated:  Return type of Sandreas\Time\TimeUnit::jsonSerialize() should either be compatible with JsonSerializable::jsonSerialize(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in phar:///opt/homebrew/bin/m4b-tool/vendor/sandreas/php-time/src/Sandreas/Time/TimeUnit.php on line 241

Deprecated: Return type of Sandreas\Time\TimeUnit::jsonSerialize() should either be compatible with JsonSerializable::jsonSerialize(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in phar:///opt/homebrew/bin/m4b-tool/vendor/sandreas/php-time/src/Sandreas/Time/TimeUnit.php on line 241
m4b-tool development, OS: Darwin ( - )
'ffmpeg' '-hide_banner' '-version'
== load input files ==
found 9 files to convert
using cover output/source/Steph Cha/Your House Will Pay/cover.jpg
skip cover extraction, file output/source/Steph Cha/Your House Will Pay/cover.jpg already exists - use --force to overwrite
preparing conversion with 4 simultaneous jobs, please wait...
'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-i' 'output/source/Steph Cha/Your House Will Pay/Track 01.mp3' '-map_metadata' '0' '-max_muxing_queue_size' '9999' '-strict' 'experimental' '-movflags' '+faststart' '-vn' '-ab' '64k' '-ar' '22050' '-ac' '0' '-acodec' 'aac' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/1-converting.m4b'
'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-i' 'output/source/Steph Cha/Your House Will Pay/Track 02.mp3' '-map_metadata' '0' '-max_muxing_queue_size' '9999' '-strict' 'experimental' '-movflags' '+faststart' '-vn' '-ab' '64k' '-ar' '22050' '-ac' '0' '-acodec' 'aac' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/2-converting.m4b'
'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-i' 'output/source/Steph Cha/Your House Will Pay/Track 03.mp3' '-map_metadata' '0' '-max_muxing_queue_size' '9999' '-strict' 'experimental' '-movflags' '+faststart' '-vn' '-ab' '64k' '-ar' '22050' '-ac' '0' '-acodec' 'aac' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/3-converting.m4b'
'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-i' 'output/source/Steph Cha/Your House Will Pay/Track 04.mp3' '-map_metadata' '0' '-max_muxing_queue_size' '9999' '-strict' 'experimental' '-movflags' '+faststart' '-vn' '-ab' '64k' '-ar' '22050' '-ac' '0' '-acodec' 'aac' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/4-converting.m4b'
   9 remaining /    9 total /wait  - compiling...
   9 remaining /    9 total /event - compiled successfully in 1986 ms (879 modules)
   9 remaining /    9 total \[2022-06-14T20:06:06.892Z]                 GETTING LOCAL BOOK BY ID: 04848da6-1fa6-4adc-bf5a-78b605940f1a-425
   9 remaining /    9 total -wait  - compiling...
   9 remaining /    9 total |event - compiled successfully in 1066 ms (879 modules)
   9 remaining /    9 total -[2022-06-14T20:06:56.643Z]                 GETTING LOCAL BOOK BY ID: 04848da6-1fa6-4adc-bf5a-78b605940f1a-425
   9 remaining /    9 total \[2022-06-14T20:07:01.259Z]                 CHECKING AVAILABLE DISK SPACE
   9 remaining /    9 total \'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-i' 'output/source/Steph Cha/Your House Will Pay/Track 05.mp3' '-map_metadata' '0' '-max_muxing_queue_size' '9999' '-strict' 'experimental' '-movflags' '+faststart' '-vn' '-ab' '64k' '-ar' '22050' '-ac' '0' '-acodec' 'aac' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/5-converting.m4b'
   8 remaining /    9 total \'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-i' 'output/source/Steph Cha/Your House Will Pay/Track 06.mp3' '-map_metadata' '0' '-max_muxing_queue_size' '9999' '-strict' 'experimental' '-movflags' '+faststart' '-vn' '-ab' '64k' '-ar' '22050' '-ac' '0' '-acodec' 'aac' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/6-converting.m4b'
   7 remaining /    9 total \'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-i' 'output/source/Steph Cha/Your House Will Pay/Track 07.mp3' '-map_metadata' '0' '-max_muxing_queue_size' '9999' '-strict' 'experimental' '-movflags' '+faststart' '-vn' '-ab' '64k' '-ar' '22050' '-ac' '0' '-acodec' 'aac' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/7-converting.m4b'
   6 remaining /    9 total /'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-i' 'output/source/Steph Cha/Your House Will Pay/Track 08.mp3' '-map_metadata' '0' '-max_muxing_queue_size' '9999' '-strict' 'experimental' '-movflags' '+faststart' '-vn' '-ab' '64k' '-ar' '22050' '-ac' '0' '-acodec' 'aac' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/8-converting.m4b'
   5 remaining /    9 total \[2022-06-14T20:08:51.830Z]                 CHECKING AVAILABLE DISK SPACE
   5 remaining /    9 total /'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-i' 'output/source/Steph Cha/Your House Will Pay/Track 09.mp3' '-map_metadata' '0' '-max_muxing_queue_size' '9999' '-strict' 'experimental' '-movflags' '+faststart' '-vn' '-ab' '64k' '-ar' '22050' '-ac' '0' '-acodec' 'aac' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/9-converting.m4b'
   0 remaining /    9 total, preparing next task /
using cover output/source/Steph Cha/Your House Will Pay/cover.jpg
ffmpeg concat file output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/tmp_Steph Cha - Your House Will Pay.m4b.listing.txt content:
------ start ------
file '/Volumes/files-xtrm/code/audiobook-scraper-web/output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/1-finished.m4b'
file '/Volumes/files-xtrm/code/audiobook-scraper-web/output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/2-finished.m4b'
file '/Volumes/files-xtrm/code/audiobook-scraper-web/output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/3-finished.m4b'
file '/Volumes/files-xtrm/code/audiobook-scraper-web/output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/4-finished.m4b'
file '/Volumes/files-xtrm/code/audiobook-scraper-web/output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/5-finished.m4b'
file '/Volumes/files-xtrm/code/audiobook-scraper-web/output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/6-finished.m4b'
file '/Volumes/files-xtrm/code/audiobook-scraper-web/output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/7-finished.m4b'
file '/Volumes/files-xtrm/code/audiobook-scraper-web/output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/8-finished.m4b'
file '/Volumes/files-xtrm/code/audiobook-scraper-web/output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/9-finished.m4b'

------ end ------
merging 9 files into output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/tmp_Steph Cha - Your House Will Pay.m4b, this can take a while
'ffmpeg' '-nostats' '-loglevel' 'panic' '-hide_banner' '-f' 'concat' '-safe' '0' '-vn' '-i' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/tmp_Steph Cha - Your House Will Pay.m4b.listing.txt' '-max_muxing_queue_size' '9999' '-c' 'copy' '-f' 'mp4' '-filter_complex' 'deesser=i=.25' 'output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/tmp_Steph Cha - Your House Will Pay.m4b'
could not merge to output/final/Steph Cha/Steph Cha - Your House Will Pay-tmpfiles/tmp_Steph Cha - Your House Will Pay.m4b
trace: #0 phar:///opt/homebrew/bin/m4b-tool/src/library/Command/MergeCommand.php(782): M4bTool\Executables\Ffmpeg->mergeFiles(Array, Object(SplFileInfo), Object(M4bTool\Executables\FileConverterOptions))
#1 phar:///opt/homebrew/bin/m4b-tool/src/library/Command/MergeCommand.php(529): M4bTool\Command\MergeCommand->mergeFiles()
#2 phar:///opt/homebrew/bin/m4b-tool/src/library/Command/MergeCommand.php(423): M4bTool\Command\MergeCommand->processInputFiles()
#3 phar:///opt/homebrew/bin/m4b-tool/src/library/Command/MergeCommand.php(184): M4bTool\Command\MergeCommand->processFiles(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#4 phar:///opt/homebrew/bin/m4b-tool/vendor/symfony/console/Command/Command.php(255): M4bTool\Command\MergeCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#5 phar:///opt/homebrew/bin/m4b-tool/vendor/symfony/console/Application.php(1009): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#6 phar:///opt/homebrew/bin/m4b-tool/vendor/symfony/console/Application.php(273): Symfony\Component\Console\Application->doRunCommand(Object(M4bTool\Command\MergeCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#7 phar:///opt/homebrew/bin/m4b-tool/vendor/symfony/console/Application.php(149): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 phar:///opt/homebrew/bin/m4b-tool/bin/m4b-tool.php(47): Symfony\Component\Console\Application->run()
#9 /opt/homebrew/bin/m4b-tool(10): require('phar:///opt/hom...')
#10 {main}

csandman avatar Jun 14 '22 20:06 csandman

I was interested in adding a desser filter to my merge implementation

Interesting idea...

So is there any way to apply filters during the initial conversion tasks, and not to the concat task?

Unfortunately, no. The extra ffmpeg params is an option for the pro users to be a bit more flexible, while limiting the extra params to specific tasks would be a lot of work to do it in a generic way. So at the moment it is either append to ALL ffmpeg commands or none. And even worse: I will not have the time to implement this in the next months while I think it might be a nice addition.

You have basically two options:

  • Option 1 (NOT recommended): Append the --debug flag which keeps the files even if something goes wrong, go half way until you have the tmp files and perform the concat command by hand
  • Option 2 (recommended): Change some code and build a custom version of m4b-tool

https://github.com/sandreas/m4b-tool/blob/801544de9e576bdf5e82d35d68f47efb1234aab3/src/library/Executables/Ffmpeg.php#L107

        // add the !in_array check to prevent adding extra argumens when concat is used
        if (count($extraArguments) > 0 && !in_array("concat", $arguments, true)) {
            $output = array_pop($arguments);
            foreach ($extraArguments as $argument) {
                $arguments[] = $argument;
            }
            $arguments[] = $output;
        }

sandreas avatar Jun 15 '22 08:06 sandreas

I will not have the time to implement this in the next months while I think it might be a nice addition.

I'm not in a real rush for this tbh, I was just experimenting in my web app which implements m4b-tool. I was discussing the process of merging audiobook files from online sources with someone on Reddit (something like OverDrive audio for example), and he mentioned that to improve the audio a bit he uses the deesser and alimiter (soft limiter) filters, but only lightly. I thought this could be a nice touch so figured I'd try it with this.

Option 1 (NOT recommended): Append the --debug flag which keeps the files even if something goes wrong, go half way until you have the tmp files and perform the concat command by hand

I could do something like this I guess, where I add the filter(s) as --ffmpeg-param args with --debug, and once it fails to concat run the merge command on the output files with the --no-conversion arg. I might look into that just to see haha.

Doing something like this manually wouldn't work in my case as I'm implementing this tool programmatically, but relying on it to fail could work...

Option 2 (recommended): Change some code and build a custom version of m4b-tool

I'd consider doing this if I had any experience with PHP, but I haven't touched it since a class I took in college 7 years ago haha. I might still take a look at it but I probably won't get far.

But either way, if you think it sounds like an interesting feature, and might consider adding it at some point (even if not for a couple months) that would be cool to see. If adding ffmpeg params specifically for the conversion part of the process is difficult, perhaps you could add a more specific option specifically for ffmpeg filters. Not sure if that would be any easier, but might be worth giving some thought.

csandman avatar Jun 15 '22 18:06 csandman

I'd consider doing this if I had any experience with PHP, but I haven't touched it since a class I took in college 7 years ago haha. I might still take a look at it but I probably won't get far.

There is not really much to do... but I understand if you see this as unnecessary shenannigans. If you have the necessary system requirements (PHP > 8, composer), it should work. Here is a little "tryout":

git clone https://github.com/sandreas/m4b-tool.git
cd m4b-tool

# edit the php file like described above

./build
ls ./dist/m4b-tool.phar

That should be it. Put the m4b-tool.phar whereever your current one is (backup the old if necessary) and it should work.

Another approach would be:

  • build a flac or a higher quality mp4 file (e.g. 256kbit)
  • convert and deesser to destination quality (e.g. 64kbit) with one single ffmpeg using -map_metadata
  • use m4b-tool export before and reimport missing information

sandreas avatar Jun 16 '22 15:06 sandreas