How to use ffmpeg stream copy within bash "for loop"? - bash

ok i have this and it works but it's slow
for i in *.mov;
do name=`echo "$i" | cut -d'.' -f1`
echo "$name"
ffmpeg -i "$i" "${name}.mp4"
done
I want it to convert with these ffmpeg options
ffmpeg -i movie.mov -vcodec copy -acodec copy out.mp4

Parameter expansion is sufficient; you don't need pipelines involving cut.
for i in *.mov; do
name=${i%%.mov} # i=foo.mov => name=foo
ffmpeg -i "$i" "$name.mp4"
done

Untested, but you should be able to do them in parallel with GNU Parallel if it's too slow and you have a multicore CPU:
parallel ffmpeg -i {} -vcodec copy -acodec copy {.}.mp4 ::: *.mov
If you want to see what it would do first, without actually doing anything:
parallel --dry-run ffmpeg -i {} -vcodec copy -acodec copy {.}.mp4 ::: *.mov
Note: On macOS, install these two packages simply with homebrew using:
brew install ffmpeg
brew install parallel

Use:
for i in *.mov; do ffmpeg -i "$i" -c copy "${i%.*}.mp4"; done
-c copy is the same as -vcodec copy -acodec copy.
Adapted from How do you convert an entire directory with ffmpeg?

Related

merge audio and video with ffmpeg doesnt work correctly

I have ubuntu 20.04 and in past days I did this job(merge video and audio) well in terminal and with ffmpeg:
ffmpeg -i input.mp4 -i input2.mp3 -c copy output.mp4
so fast I have recived output.mp4, but now I tried this one and get output without any sound!
I try another ways to merge this ones(also with ffmpeg) but there are no diffrent...
ffmpeg -f concat -safe 0 -i <(for f in ./input*.mp4; do echo "file '$PWD/$f'"; done) -c copy output.mp4
Note -f concat will select a demuxer. This alters the way -i nput files are read.
So instead video-files 'concat expects a txt-file listing the files to concatenate.
However we somehow omit the creation of that text file and use process substitution to generate and pass that list on the fly to demux.
For more details go here:
https://trac.ffmpeg.org/wiki/Concatenate#demuxer
If you want to merge several video files, you can use these command.
merge two video files.
ffmpeg -f concat -i 1.mp4 -1 2.mp4 -codec copy out.mp4
merge multiple video files.
ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mpt -vcodec copy -acodec copy out.mp4

Using ffmpeg to convert mp3 to mp4 - how to draw filename on generated video?

Using trial and error I am using the following script on an OSX, to bulk convert a whole folder full of mp3 files, to mp4, by looping a specific video file:
for i in *.mp3; do /usr/local/bin/ffmpeg -stream_loop -1 -i /path_to_filename.mp4 -c copy -v 0 -f nut - | /usr/local/bin/ffmpeg -thread_queue_size 10K -i - -i "$i" -c copy -map 0:v -map 1:a -shortest "$(basename "$i" )".mp4 ; done; for f in *.mp3.mp4; do mv -v "$f" "${f/.mp3.mp4/.mp4}"; done
How can I also print/add/burn the mp3 filename, without the extension (.mp3), as an additional video layer at the bottom of the generated video screen, and with the added difficulty of word wrapping the text if is too long?
This command makes a temporary SRT file for the subtitles filter which will automatically deal with the placement and word wrapping:
for i in *.mp3; do echo "1" > subs.srt; echo "00:00:00,000 --> 10:00:00,000" >> subs.srt; echo "${i%.*}" >> subs.srt; ffmpeg -stream_loop -1 -i video.mp4 -i "$i" -filter_complex "[0:v]subtitles=subs.srt:force_style=Alignment=3,format=yuv420p[v]" -map "[v]" -map 1:a -c:v libx264 -c:a copy -shortest -movflags +faststart "${i%.*}.mp4"; done
Running the command in the directory containing the file allows avoidance of basename and creates a simpler command.
MP3 is not universally supported in MP4. Consider changing -c:a copy to -c:a aac if your player does not support it. But I suspect you're targeting YouTube which will be fine with MP3 in MP4.
The three instances of echo are inefficient but effective in avoiding any newline issues. Using one instance of printf would be more optimal, but I don't have access to macOS to test its printf implementation.

/usr/local/bin/ffmpeg: cannot execute binary file

Want to convert my m4a files into mp3 files using a script. It would save some time... I have over 100 GB of music files.
OS: OSX10.14 / Terminal vs Bash script
I can run ffmpeg -v 5 -y -i musicFile.m4a -acodec libmp3lame -ac 2 -b:a 320k musicFile.mp3 from the terminal. It converts the file and I can see and play the file from itunes.
When I run the same from a bash script it fails to convert.
ffmpeg -v 5 -y -i $ENTRY_FILE -acodec libmp3lame -ac 2 -b:a 320k $MP3NAME
My ipod nano just died and I got a new mp3 player. Now I need to convert my itunes files from AAC format to MP3.
ffmpeg is an established video and music file converter.
When I run it from the bash script I tried a few things.
I added ./ in front of the file, that failed because it was installed under /usr/local/bin and not under the same directory.
I also tried sh ffmpeg... and that gave me the cannot execute a binary file.
#!/usr/bin/env bash
# convert m4a file to mp3
set -e
file_convert() {
ENTRY_FILE=$(printf %q "${entry}")
FILE_NAME=$(printf %q "$(basename "${entry}")")
DIR=$(printf %q "$(dirname "${entry}")")
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
MP3NAME="${DIR}/${NAME}.mp3"
printf "%*s%s\n" $((indent+2)) '' "$ENTRY_FILE"
printf "%*s\tNew File :\t%s\n" $((indent+2)) '' "$MP3NAME"
if [ $EXT == "m4a" ]
then
printf "%*s\tConverting: \t%s\n" $((index+2)) '' "$ENTRY_FILE"
ffmpeg -v 5 -y -i $ENTRY_FILE -acodec libmp3lame -ac 2 -b:a 320k $MP3NAME
fi
}
walk() {
local indent="${2:-0}"
printf "\n%*s%s\n\n" "$indent" '' "$1"
# If the entry is a file convert it
for entry in "$1"/*; do [[ -f "$entry" ]] && file_convert; done
# If the entry is a directory recurse
for entry in "$1"/*; do [[ -d "$entry" ]] && walk "$entry" $((indent+2)); done
}
# If the path is empty use the current, otherwise convert relative to absolute; Exec walk()
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || pushd "${1}" && ABS_PATH="${PWD}"
walk "${ABS_PATH}"
popd
echo
I expect >./aacToMp3.sh ./music to traverses the music directory and convert each m4a file to .mp3.
It is walking the file system and printing out correct files, with the spaces escaped. When it hits the ffmpeg line it halts. I put the set -e at the top of the file to force it to fail if the command fails. Without the set -e it happily walks all the music files and prints them to the stdout.
If you have lots of files to process and a decent multi-core CPU and fast disk, I would recommend GNU Parallel which you can install with homebrew:
brew install parallel
Then make a copy of a few files in a test directory and try:
parallel --dry-run ffmpeg -v 5 -y -i {} -acodec libmp3lame -ac 2 -b:a 320k {.}.mp3 ::: *.m4a
If that looks good, replace --dry-run with --progress.
If that looks good, you can (make a backup first) and do the whole lot:
find path/to/music -name "*.m4a" -print0 | parallel -0 --progress ffmpeg -v 5 -y -i {} -acodec libmp3lame -ac 2 -b:a 320k {.}.mp3
Thanks for all the input.
After a while I ended up writing all the ffmpeg lines to a script file. Glad I did. I was able to quickly scan the file and see some errors and fix them.
This is what I came up with.
Basically I am writing straight text to stdout and directing it to a file. Which I converted to a shell script to convert each file. One at a time. Ran the generated script overnight.
file_convert() {
ENTRY_FILE=$(printf %q "${entry}")
FILE_NAME=$(printf %q "$(basename "${entry}")")
DIR=$(printf %q "$(dirname "${entry}")")
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
MP3NAME="${DIR}/${NAME}.mp3"
if [ $EXT == "m4a" ]
then
printf 'echo "Converting %s ..."\n' "$FILE_NAME"
printf 'ffmpeg -v 5 -y -i %s -acodec libmp3lame -ac 2 -b:a 320k %s\n\n' "$ENTRY_FILE" "$MP3NAME"
fi
}
The output looked like.
#!/usr/bin/env bash
echo "Converting 03\ It\'s\ Not\ My\ Time.m4a ..."
ffmpeg -v 5 -y -i /Users/arthuranderson/Documents/work/projects/mp3Convert/music/3\ Doors\ Down/3\ Doors\ Down\ \(Bonus\ Track\ Version\)/03\ It\'s\ Not\ My\ Time.m4a -acodec libmp3lame -ac 2 -b:a 320k /Users/arthuranderson/Documents/work/projects/mp3Convert/music/3\ Doors\ Down/3\ Doors\ Down\ \(Bonus\ Track\ Version\)/03\ It\'s\ Not\ My\ Time.mp3
echo "Converting 01\ Down.m4a ..."
ffmpeg -v 5 -y -i /Users/arthuranderson/Documents/work/projects/mp3Convert/music/311/Greatest\ Hits\ \'93-\'03/01\ Down.m4a -acodec libmp3lame -ac 2 -b:a 320k /Users/arthuranderson/Documents/work/projects/mp3Convert/music/311/Greatest\ Hits\ \'93-\'03/01\ Down.mp3
Mark I will try parallel when I purchase some more music next time.
Thanks everyone!

ffmpeg with "-pattern_type glob" and variable in bash script

I'm trying to let ffmpeg make a video of all pictures in a directory using the -pattern_type glob switch and "/foo/bar/*.jpg". This works well, if I execute the command manually für just one directory. For example:
ffmpeg -framerate 35 -pattern_type glob -i '/root/webcam_test/2018-07-21/*.jpg' -vf scale=1280:-1 -c -c:v libx264 -pix_fmt yuv420p /root/clips/out01_cut.mp4
However, if I do it in a bash script and set the path via a variable, according to ffmpegs output, the variable gets substituted correctly, but ffmpeg states that
'/root/webcam_test/2018-07-21/*.jpg': No such file or directory
The part of the script looks like this:
for D in `find /root/webcam_test/ -type d`
do
[...]
cmd="ffmpeg -framerate 35 -pattern_type glob -i '$D/*.jpg' -vf scale=1280:-1 -c -c:v libx264 -pix_fm t yuv420p /root/clips/$d_cut.mp4"
echo $cmd
[...]
done
Does anyone know how to make ffmpeg do its wildcard interpretation even if the path is constructed by a script and not just try to plainly use the given path?
Best regards and thanks in advance
I had similar problem. My solution is to change directory before ffmpeg command and use pattern without directory. i.e.:
cd $D/;ffmpeg -framerate 35 -pattern_type glob -i "*.jpg" -vf scale=1280:-1 -c -c:v libx264 -pix_fmt yuv420p /root/clips/$D_cut.mp4
By putting the bash variable inside single quotes, it does not get expanded. Try with double quotes:
ffmpeg -framerate 35 -pattern_type glob -i "$D/*.jpg" -vf scale=1280:-1 -c -c:v libx264 -pix_fmt yuv420p /root/clips/$D_cut.mp4
Either of these, quoted or not, work in my timelapse cron script:
$ ls -1 *.jpg
20190620_011712sm.jpg
$ TODAY=$(date +"%Y%m%d")
$ ffmpeg -y -v 24 -f image2 -pattern_type glob -i ${TODAY}_\*sm.jpg -r 12 -vcodec libx264 ${TODAY}.mp4 2>&1|col -b
$ ffmpeg -y -v 24 -f image2 -pattern_type glob -i "${TODAY}_*sm.jpg" -r 12 -vcodec libx264 ${TODAY}.mp4 2>&1|col -b
I don't know why, but I fought hard to use single quotes around the * char before I came upon this. ...Lack of sleep maybe?

Running FFMPEG from Shell Script /bin/sh

I am trying to setup a Shell Script to work within an automator watch folder...
Everything works with the exception of the Run Shell Scrip portion...
Essentially when a file shows up in the watch folder, it runs the shell scrip which calls FFMPEG and then will move the file to an archive folder for safe keeping. However right now automator is telling me everything worked but now file is being created.
I have the Shell set to /bin/sh and Pass input set to as arguments
Here is my script:
for f in "$#"
do
name=$(basename "$f")
dir=$(dirname "$f")
ffmpeg -i "$f" -b 250k -strict experimental -deinterlace -vcodec h264 -acodec aac "$dir/mp4/${name%.*}.mp4"
echo "$dir/mp4/${name%.*}.mp4"
done
it does echo the correct filename, but does not actually run ffmpeg
I have tried adding -exec before it like I have seen in some scripts but still nothing...
FFmpeg searches STDIN while it is running. This is to allow the user to hit q to stop encoding, among other tasks. This could cause a problem with a script. Perhaps to workaround try this:
# notice --+
# |
# v
ffmpeg -nostdin -i "$f" -b 250k -strict experimental -deinterlace \
-vcodec h264 -acodec aac "$dir/mp4/${name%.*}.mp4"
mysterious error with ffmpeg on OSX
Not sure if it is because I am running on a Mac OS X Server but I imagine it is, I had to include the absolute path to ffmpeg...which fixed it
for f in "$#"
do
name=$(basename "$f")
dir=$(dirname "$f")
/opt/local/bin/ffmpeg -i "$f" -b 250k -strict experimental -deinterlace -vcodec h264 -acodec aac "$dir/mp4/${name%.*}.mp4"
echo "$dir/mp4/${name%.*}.mp4"
done

Resources