Issue with overwriting file while using ffmpeg for converting - ffmpeg

I'm using ffpmeg to convert all my videos to mp4:
ffmpeg -i InpuFile -vcodec h264 -acodec aac -strict -2 OutputFile.mp4
The problem is, if I'm overwriting the input file, i.e the output and input files are the same:
ffmpeg -i InpuFile -vcodec h264 -acodec aac -strict -2 InpuFile.mp4 -y
or
ffmpeg -i InpuFile -y -vcodec h264 -acodec aac -strict -2 InpuFile.mp4
the new file is not good. He lasts one second and his size is extremely small.
Any ideas?
I want to use this as a script in my server so the overwriting is the most convinient way for me, I prefer that way instead of creating temporary files then replacting the temporary with original.

I had this same (frustrating) problem, you may have noticed that this happens because ffmpeg is writing over the file that it's reading, you are corrupting the source before the process finish... ffmpeg doesn't put the file in some buffer, so you can't do this way, you will have to use a temporary file.
just in case

You cannot overwrite the input file while you are encoding. You must encode to an different output file.
Afterwards, you can replace the original file with the new encoded file.

As others have mentioned, there is no way to do this without creating a temp file. You mentioned that you wanted to compress all your videos, and for it to be convenient. Here is a bash one-liner I used to compress all MP4 & MOV inside a directory:
find * -type f \( -iname \*.mp4 -o -iname \*.mov \) -execdir ffmpeg -i {} -vcodec libx265 -crf 24 temp_{} \; -execdir mv temp_{} {} \;
The -crf param controls the video bitrate. It's value ranges from 18-24, lower value is higher bitrate.
If you just wanted to compress .mp4 for example then you'd change the command to:
find * -type f -iname "*.mp4" -execdir ffmpeg -i {} -vcodec libx265 -crf 24 temp_{} \; -execdir mv temp_{} {} \;
Hope this helps OP, or anyone looking to do something similar.

Neither is too annoying tmp file use. In one line:
input="InpuFile"; ffmpeg -i "$input" -y -vcodec h264 -acodec aac -strict -2 "/tmp/$input";rm "$input"; mv "/tmp/$input" .;

Related

How to make ffmpeg delete original file after conversion?

I'd like to convert all .mp4 movies in a folder and delete the old one afterwards.
Does anyone have a hint? I've been trying for hours.
The only thing I found is:
How to make ffmpeg delete the original file after changing containers? (using a send to bat file)
my idea:
ffmpeg -i *.mp4 -c:v libx264 -b:v 1.5M -c:a aac *.mp4
It asks if files can be overwritten, but then it doesn't:https://pastebin.com/tJtWpm2n
In ffmpeg you can't directly write to the same file you're currently reading from, but one thing you can do instead is write to a temporary file, then replace the original if ffmpeg converted successfully.
for f in *.mp4; do
ffmpeg -i "${f}" -c:v libx264 -b:v 1.5M -c:a aac "tmp_${f}" && mv "tmp_${f}" "${f}"
done
So ffmpeg reads from variable ${f} containing the original filename matched in the *.mp4 pattern and writes to tmp_${f}, then && tests ffmpeg exited successfully before replacing the original file with mv.
You might also want to ensure "tmp_${f}" does not exist first, which only takes a few more steps.
for f in *.mp4; do
tmpf=$(mktemp -p ./ -t "tmp.XXXXXXXXXX.${f##*.}") # can now be extended for any file extension
ffmpeg -i "${f}" -c:v libx264 -b:v 1.5M -c:a aac "${tmpf}" && mv "${tmpf}" "${f}"
done

Using find / for how do I remove the original file extension for the output file name?

When using find or for to run things on multiple files, how would I make something not keep the file extension?
For example if using ffmpeg on multiple files to convert from DTS to WAV I would run one of the following:
find . -name "*.dts" -exec ffmpeg -i "{}" -c:a pcm_s24le "{}.wav" \;
or
for f in ./*.dts; do ffmpeg -i "$f" -c:a pcm_s24le "$f.wav"; done
Both of these make files that end in .dts.wav rather than just .wav
My goal is to find out what I would add/change to make the "{}.wav" or "$f.wav" not include the .dts part for the output file name. (and several other examples with various extensions)
This happens automatically when using the cli version of flac, the output file automatically removes .wav and has .flac instead, when no output file is specified.
(Ex: flac -8 *.wav would create .flac files next to the .wav files, but they aren't .wav.flac, they're just .flac)
You might want to use GNU parallel for this, e.g.:
find . -name '*.dts' | parallel 'echo ffmpeg -i {} -c:a pcm_s24le {.}.wav'
Remove echo when you want to execute the commands. You can control how many jobs run simultaneously with -j N.
Example
mkdir a b
touch [ab]/infile.dts
Check file-structure:
find a b
Output:
a
a/infile.dts
b
b/infile.dts
Now with parallel:
find a b -name '*.dts' | parallel 'echo ffmpeg -i {} -c:a pcm_s24le {.}.wav'
Output:
ffmpeg -i a/infile.dts -c:a pcm_s24le a/infile.wav
ffmpeg -i b/infile.dts -c:a pcm_s24le b/infile.wav

Change ffmpeg output directory

Im using ffmpeg to compress footage and i want to compess the footage of a specific day but when i overwrite the files it outputs a empty stream because it writes as it reads at the same time so i want to rename the output file. Find will give the full path which is necessary but i don't know how to change the actual file name, rather than the path.
Any suggestions?
find /home/server/recordings/compress -name '*.mp4' -print0 | xargs -0 -I{} ffmpeg -i {} -c:v libx265 -preset fast -crf 25 -x265-params "vbv-maxrate=1500:vbv-bufsize=1000" -c:a aac {}
The last argument in ffmpeg is the output filename. So you can change your command to
find /home/server/recordings/compress -name '*.mp4' -print0 | xargs -0 -I{} ffmpeg -i {} -c:v libx265 -preset fast -crf 25 -x265-params "vbv-maxrate=1500:vbv-bufsize=1000" -c:a aac {}.out
This way all the output files will have .out appended.
Pass file names to sh using -exec and modify the filename there. For example:
find /home/server/recordings/compress -name '*.mp4' -exec sh -c '
ffmpeg -i "$1" -c:v libx265 -preset fast -crf 25 -x265-params "vbv-maxrate=1500:vbv-bufsize=1000" -c:a aac \
"${1%/*}/modified_${1##*/}"
' _ {} \;

Recursively re-encode .mp4 files to new copies of .mp4 and convert .mp4 to .webm and .ogg using ffmpeg

I have about 300 videos in .mp4 files that I need re-encode as new .mp4 files and to convert them to .webm and .ogg files.
I want to do it at the command line using ffmpeg, and I have the following command that converts the .mp4 into a .webm.
find ./ -name '*.mp4' -exec bash -c 'ffmpeg -i "$0" -vcodec libvpx -acodec libvorbis -cpu-used 5 -threads 8 "${0%%.mp4}.webm"' {} \;
Can someone help me modify this command to two separate commands, one for .mp4 -> .mp4 (suffixing the filename with -2) and another for .mp4 -> .ogg?
Thank you.
Well, as I didn't get any replies, I did find the answer myself.
So, for the benefit of others looking to do the same thing, here are the various commands I put together:
webm
find ./ -name '*.mp4' -exec bash -c 'ffmpeg -i "$0" -vcodec libvpx -acodec libvorbis -vf scale=-1:480 -cpu-used 5 -threads 8 "${0%%.mp4}.webm"' {} \;
ogv
find ./ -name '*.mp4' -exec bash -c 'ffmpeg -i "$0" -vcodec libtheora -acodec libvorbis -vf scale=-1:480 -cpu-used 5 -threads 8 "${0%%.mp4}.ogv"' {} \;
flv
find ./ -name '*.mp4' -exec bash -c 'ffmpeg -i "$0" -c:v libx264 -ar 22050 -crf 28 -vf scale=-1:480 -cpu-used 5 -threads 8 "${0%%.mp4}.flv"' {} \;
mp4
find ./ -name '*.mp4' -exec bash -c 'ffmpeg -i "$0" -vcodec libx264 -vf scale=-1:480 -cpu-used 5 -threads 8 "${0%%.mp4}-2.mp4"' {} \;
jpg
find ./ -name '*.mp4' -exec bash -c 'ffmpeg -i "$0" -ss 00:00:10 -vframes 1 -r 1 -vf scale=-1:480 -f image2 "${0%%.mp4}.jpg"' {} \;
Note that I have added the flag -vf scale=-1:480 which scales the video proportionately. I have set the height to 480px and the width is automatically calculated.
Also, note that I have also included a screenshot export from each video. The capture is made at the 10th second of the video and is saved as a jpeg file.
If you would like to retain the dates of the original files so that the new version (.webm, .ogv, .mp4, .flv) have the same modification dates, you can use the touch command as follows:
touch -r oldfile newfile

How can I automatically convert all MP4 files to FLV with ffmpeg?

How can I automatically convert all MP4 files to FLV in a specific folder?
ffmpeg -i VID00002.MP4 -ar 44100 test.flv
Is there a way to queue these tasks, assuming that I don't know the file names?
If I need to run any scripts (I'm familiar with Python), how can I do that?
You can do this fairly easy within the terminal, given you have ffmpeg installed. In your terminal, enter the following:
$>cd /your/path/to/videos
$>for i in *.mp4; do ffmpeg -i $i -ar 44100 $i.flv; done
The second command simply iterates through each mp4 file and assigns the filename to '$i'. You then call ffmpeg using $i as the input and output filename. For the output, you simply add the extension, in this case $i.flv. So, if your filename is 'video.mp4', it will output as 'video.mp4.flv'.
Hope this helps.
This will convert and rename the new files using the find and ffmpeg functions and suppressing output questions:
find /mymediapath (\ -name '*.mp4' \) -exec bash -c 'ffmpeg -y -i "$0" -strict -2 "${0/mp4/flv}"' {} \;

Resources