ZSH: Get basename for for loop - for-loop

I'm trying to get the basename (without file extension) so that I can process a directory of audio files (.flac -> .m4a) quickly and and up with proper filenames.
So this works:
for i in *.flac;do ffmpeg -i $i -c:a libfdk_aac -vbr 3 $i.m4a; done
But it leaves me with a file like like this:
audiofile.flac.m4a
What I would like to do is just grab the "audiofile" part of the filename so that this works:
for i in *.flac;do ffmpeg -i $i.flac -c:a libfdk_aac -vbr 3 $i.m4a; done
But I don't understand modifiers (apparently a ":t" modifier might get me the results?) or how to define variables on the for loop (for each file). Also, I see that there are many examples of how to do this with BASH, but I'm looking for something that works with zsh.
Any ideas?
Thanks!

In zsh you can use the Modifiers from History Expansion with Parameter Expansions. Each modifier is preceded by a colon: ${name:modifier}. Thermodifier removes the extension, i.e. everything from - including - the last.` to the end of the file name:
for i in *.flac; do
ffmpeg -i $i -c:a libfdk_aac -vbr 3 ${i:r}.m4a
done
Note: the modifier r will remove any extension, not only ".flac".
A more portable (read: should work in any POSIX-compliant shell) and specific solution would be to use the Parameter Expansion ${name%pattern}, which will remove the smallest string matching pattern from the end of the value of name. If pattern does not match the end of the value of name, the value will be returned unchanged:
for i in *.flac; do
ffmpeg -i $i -c:a libfdk_aac -vbr 3 ${i%.flac}.m4a
done
This will remove only ".flac" specifically from the end of the file names.

Related

ffmpeg - Converting series of images to video (Ubuntu)

I got pictures named as
pic_0_new.jpg
pic_10_new.jpg
pic_20_new.jpg
...
pic_1050_new.jpg
which I want to turn into a video (Ubuntu ffmpeg). I tried the following
ffmpeg -start_number 0 -i pic_%d_new.jpg -vcodec mpeg4 test.avi
but I don't know how to set the step size and the end number. How to do this?
Thanks for help :)
If your files are named with leading zeroes then you can use the built-in globbing functionality. If not (like your's), you can create a file list and supply that as the input, just like explained here.
The other thing you need to set is the input framerate that tells FFmpeg what framerate should assume for the images (note that the option is ahead of the -i input option).
So the command should look like this:
ffmpeg -framerate 25 -i pic_%04d_new.jpg <encoding_settings> test.avi
Also note that you can use the filename expansion on files with or without leading zeroes (Thanks for #Gyan to pointing it out):
match regularly numbered files: %d (this is what you're using)
img_%d.png // This will match files with any number as a postfix starting from img_0.png
match leading zeroes: %0<number_of_digits>d
img_%03d.png // This will match files ranging from img_000.png to img_999.png
In addition, mpeg4/avi is not the most convenient encoder/container to use...

ffmpeg convert images with name pattern into video [duplicate]

I am trying to make an FFMPEG script that relied on a glob input pattern from Linux to Windows. Unfortunately that is not supported so I am looking for an alternative. I do not want to have to rename or copy the files every time I run the script because the files are used elsewhere and I cannot rename them and I would like to avoid duplication or unnecessary temporary files.
Are globs numerically sequential named images my only option here? Ideally I would like to input a list of image paths to FFMPEG as a substitute for ffmpeg -i *.jpg
The workarounds are to prepare a text file with the names and use the concat demuxer.
Or you can use image2pipe
cat *.jpg | ffmpeg -f image2pipe -framerate 25 -i - out.mp4
The best solution I could find (that's Windows compatible) was to generate a line separated list of files in a text file and pass that through to FFMPEG. For example, to generate a stabilized MP4 from a bunch of JPEGs:
ffmpeg -f concat -safe 0 -i ./files.txt -vf deshake=rx=64:ry=64 ./stabilized.mp4
Where files.txt is a list of the files in the following format. The safe option toggles the ability to have absolute/relative file paths.
# this is a comment
file 'C:/path/to/file1.jpg'
file 'C:/path/to/file2.jpg'
file 'C:/path/to/file3.jpg'

Input parameters to FFMPEG

I am trying to make an FFMPEG script that relied on a glob input pattern from Linux to Windows. Unfortunately that is not supported so I am looking for an alternative. I do not want to have to rename or copy the files every time I run the script because the files are used elsewhere and I cannot rename them and I would like to avoid duplication or unnecessary temporary files.
Are globs numerically sequential named images my only option here? Ideally I would like to input a list of image paths to FFMPEG as a substitute for ffmpeg -i *.jpg
The workarounds are to prepare a text file with the names and use the concat demuxer.
Or you can use image2pipe
cat *.jpg | ffmpeg -f image2pipe -framerate 25 -i - out.mp4
The best solution I could find (that's Windows compatible) was to generate a line separated list of files in a text file and pass that through to FFMPEG. For example, to generate a stabilized MP4 from a bunch of JPEGs:
ffmpeg -f concat -safe 0 -i ./files.txt -vf deshake=rx=64:ry=64 ./stabilized.mp4
Where files.txt is a list of the files in the following format. The safe option toggles the ability to have absolute/relative file paths.
# this is a comment
file 'C:/path/to/file1.jpg'
file 'C:/path/to/file2.jpg'
file 'C:/path/to/file3.jpg'

Why isn't my bash script working?

I'm trying to get this bash script to work but am at a loss. I have a text file that contains a list of frame numbers line by line. ffmpeg reports the error:
Undefined constant or missing '(' in '$name)'
The script
#!/bin/bash
source text.txt
while read name
do
ffmpeg -i result.mp4 -vf "setpts=N+1,select='eq(n,\$name)'" -vframes 1 frame-$i.jpg
done <text.txt
You are escaping the $ before variable name i.e. $name, so the $name will be treated literally without any variable expansion being done.
Do:
ffmpeg -i result.mp4 -vf "setpts=N+1,select='eq(n,$name)'" -vframes 1 frame-$i.jpg

Need explanation of details of ffmpeg and pipes command

Got the following from FFmpeg FAQ:
mkfifo intermediate1.mpg
mkfifo intermediate2.mpg
ffmpeg -i input1.avi -sameq -y intermediate1.mpg < /dev/null &
ffmpeg -i input2.avi -sameq -y intermediate2.mpg < /dev/null &
cat intermediate1.mpg intermediate2.mpg |\
ffmpeg -f mpeg -i - -sameq -vcodec mpeg4 -acodec libmp3lame output.avi
Before i use or modify it I would like to understand it completely.
What does the < /dev/null & do?
I understand | is pipe but why |\ ?
What is the -f mpeg after ffmpeg (Seems, it tells ffmpeg to accept the piped in output from the cat(?) )
< /dev/null &
This is actually two parts:
< /dev/null
&
1 (< /dev/null) is just a simple way to pass no input/EOF to a program. I'm not sure it's needed but it may be because you are using named pipes.
2 (&) simply pushes the command to the background and allows you to do other things. This is necessary because otherwise, ffmpeg would just sit there waiting for the other end of the named pipe to "open".
Backslash after pipe
The backslash after the pipe is simply there to allow you to enter the long command on multiple lines. If you want to write it on a single line, you should omit the backslash. You'll notice that the prompt changes from your usual [user#machine directory]$ (or whatever) to something like > after you enter the first line (ending with a backslash). This signifies that your command is being continued from an earlier line.
ffmpeg -f switch
The man page for ffmpeg indicates that the -f switch allows you to force a file format. In the example in the FAQ, you want to force an input format (read: tell ffmpeg what input format to expect) since your using piped bits as input. Usually, it would try to guess the input format based on the file extension and/or "file magic".

Resources