There seems to be a bug in my bash script, and after a long time I managed to reduce it to this test case:
find . -maxdepth 1 | while read blah
do
echo "$blah"
ffmpeg -loglevel error -i ./test.jpg -f null /dev/null
done
the output from this is
/test.jpg
/test.mp4
/test.sh
if I remove the ffmpeg invocation, the output becomes this (what I expected):
./test.jpg
./test.mp4
./test.sh
this seems to occur only when the ffmpeg decoder is activated, as ffmpeg -version doesn't produce the error. Why would ffmpeg affect an unrelated string in this way?
I'm at my wit's end, any help would be appreciated.
FFmpeg is eating your standard input. Do like this instead:
find | while read
do
ffmpeg -nostdin
done
Creating forks of `ffmpeg` in loop inside a shell script "loses" some iterations
Related
This question already has answers here:
Extract filename and extension in Bash
(38 answers)
Closed 10 months ago.
I successfully outputted the desired effect for my FFMPEG command. I had been removing the audio from my videos with this line of code:
for i in *.mov; do ffmpeg -i $i -c:v copy -an $i-noaudio.mp4; done
The output results as "myInputvideo.mov-noaudio.mp4"
How might I get this to output without the ".mov" in the title?
And how might I move all these outputs into a new directory entitled "no-audio"
Thank you!
Use Parameter/Variable Expansion. instead of $i, use ${i%.mov} as follows:
Note: I replaced the variable i with file, for clarity.
#!/bin/bash
for file in *.mov; do
ffmpeg -i ${file} -c:v copy -an ${file%.mov}-noaudio.mp4
done
Here is a good reference for Variable Expansion: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
Why doesn't this work:
vim -d <(ffmpeg -i vid1.mp4 2>&1) <(ffmpeg -i vid2.mp4 2>&1)
and how can I get it to work?
Currently it just clears my screen completely and causes my terminal to freeze, completely unresponsive to everything ctrl-c ctrl-d and ctrl-z. I have to quit my terminal every time.
You must use ffprobe (comes with ffmpeg) if you want an output suitable for diffing:
$ vim -d <(ffprobe -i vid1.mp4 2>&1) <(ffprobe -i vid2.mp4 2>&1)
Why?
ffmpeg is a media converter that outputs a lot of things during processing, including some information on the source file. Using it without providing an output file/stream/whatever only to get information on the source file is not how it is supposed to be used and, well… it just doesn't work correctly anyway: you get your information but the terminal may be left in a weird state and the operation returns a non-zero status.
By using ffmpeg, you are essentially relying on a side-effect of using the wrong tool incorrectly.
ffprobe, on the other hand, exists specifically for getting information on the source file.
By using ffprobe, you are relying on the expected outcome of using the right tool correctly.
That said, ffprobe probably shares a lot of code with ffmpeg because you need that 2>&1 hack to make its output Vim-friendly. Oh well…
So, I made a script for Cygwin that uses Windows's ImageMagick and FFmpeg, but I am not sure if the results here will also apply for bash on Linux. So, what the script does is I have some cartoon video files and I'm using Waifu2x to enhance and upscale the images to 4K, and then using ImageMagick to pipe it to FFmpeg, which is also used to resize it to 3840x2160 in case the resolution is slightly different. Here's a small script I wrote for this example to simplify how it outputs to FFmpeg, as the real script is extremely lengthy and complex.
#!/bin/bash
fun(){
convert out.png JPG:-|tee "$outfile"
}
fun|ffmpeg -f image2pipe -r 60 -i - -c:v libx265 -movflags +faststart "$outputfile"
Now, what I noticed is that if FFmpeg fails to encode, the function continues but fails to output to $outfile. What I want to do is have it able to output to that file in case the encoding fails since I also write all the images to a cache folder for FFmpeg to run through in case the encoding fails, but I also want to write to both the pipe for FFmpeg and the file at the same time. What seems to be happening is that the command tee appears to be refusing to write to the file if it can't write to the pipe. I'm not sure if this behavior is intended, and/or if it also does this on Linux bash. How can I get around this and have it write to the file even if it can't write to the pipe, but write to both at the same time rather than writing to the file and attempting to read it back to the pipe?
Have you tried tee with the -p option? It makes tee continue writing even if tee can't write to its standard output, which in your case means it should cope if ffmpeg fails.
fun() {
convert out.png JPG:- | tee -p "$outfile"
}
I am trying to use youtube-dl to get the urls of some videos and then pipe the resulting urls into the input of my script. So in my terminal I do
youtube-dl --ignore-config -iga ~/Desktop/youtube/videolist.txt | myscript.sh
In my script I define things as
command='ffmpeg'
inputArgs='-i'
outputArgs='-c:v libx264 -preset ultrafast -qp 0'
directory="${HOME}/Desktop/Videos/"
output="video${count}"
extension='mp4'
I test it with echo to make sure everything appears in the correct order.
echo "${command}" "${inputArgs}" "${input}" "${outputArgs}" \
"${directory}""${output}${count}"."${extension}"
And the output from that looks correct. But when I try to run the same thing without the preceding echo command, i.e.,
"${command}" "${inputArgs}" "${input}" "${outputArgs}" \
"${directory}""${output}${count}"."${extension}"
I get an error message that says
At least one output file must be specified.
So it seems pretty obvious to me that I'm doing something wrong when attempting to execute it.
I have tried:
quoting the entire line as a whole
quoting different sections together
using the exec command in front of everything
No matter what I do, an error occurs at some point in the process. I know it's something simple I'm doing wrong. Would someone please enlighten me as to what that might be?
I feel very strongly that the . shouldn't just be in the middle of everything like that, but I really don't know.
Again, everything looks as it should when I run echo before the string of shell parameters.
If more of the script I'm using is needed to understand what I'm talking about, that is not a problem.
The problem is that because you put it in quotes "${outputArgs}" is expanded as a single argument. It doesn't get split up into separate arguments so ffmpeg only see it as a single -c option with a really long stream specifier. The next argument, the output file is interpreted as the codec instead.
To fix the problem simply remove the quotes:
"$command" $inputArgs "$input" $outputArgs "$directory$output$count.$extension"
I removed the curly braces ({}) just to save space. There's nothing wrong with using them if you prefer.
You tried to rely on a string to convey multiple arguments. You probably would want to use an array in all cases like this. An array is easy to use and more versatile (works with any arbitrary strings) and you don't have to walk on that many eggshells in order to avoid quirks and security holes, unlike when leaving quotes off.
command='ffmpeg'
inputArgs=(-i)
outputArgs=(-c:v libx264 -preset ultrafast -qp 0
-metadata 'title=another * string'
-metadata 'artist=; echo "Just a string'
-metadata "comment=Processed by my ${command} script.")
directory="${HOME}/Desktop/Videos/"
output="video${count}"
extension='mp4'
outputArgs+=(-metadata "track=${count}")
When expanding an array, the reference must have the {} around it. When used in the form: "${name[#]}", it behaves as if you had typed the contents on the line directly.
printf '%q\n' is a more useful way of examining a command compared to echo, as it can clearly tell you what belongs to which separate argument.You can also expand an array into another array:
whole_thing=("${command}" "${inputArgs[#]}" "${input}"
"${outputArgs[#]}"
"${directory}/${output}"."${extension}")
#This will output lines you can paste
# into bash to recreate the array:
printf 'whole_thing=()\n'
printf 'whole_thing+=(%q)\n' "${whole_thing[#]}"
#This will run the command:
"${whole_thing[#]}"
I have a very large audio mp4 file that contains several songs.
I have generated a script which reads a text file with the times and the song names and successfully assigns starttime, endtime and songtitle in 3 variables. The script successfully echoes the variables and returns the following format:
00:00:00 00:10:15 Song1
00:10:15 00:14:20 Song2
and so on...
Now I am intending to use this script with ffmpeg and crop each part of the big file into smaller audio files.
The script thus, after feeding the variables in a while loop, it reaches to the command
ffmpeg -ss $START -t $END -i ${1} -acodec copy $SONGNAME.mp4
Once I run the script, the first two songs are cropped, but then the whole process stops with
Press [q] to stop, [?] for help
error parsing debug value
debug=0
I checked the generated files and they play ok, but there is no way for me to know why the script stopped there and did no proceed to the rest of the file (considering that when in the script I replace ffmpeg with echo, the script echoes the variables flawlessly).
In other words I don't know if there is a problem in my script, ffmpeg, or the source music file.
In this case I would add the argument -nostdin to ffmpeg.