I have folders within folders that have movie files, some of them are .mp4, .avi or .mov/.MOV. I need to re-compress them into H264 High Quality (Lets say, 10MBPS) and delete the originals when complete. I have the following code, but this is for audio and I don't know where to go from here. I am using Mac terminal.
for i in *.ogg; do ffmpeg -i "$i" -b:a 320000 "${i%.*}.mp3"; done
Here is a small example on how this can convert mp4 files to 10M h264 files, which can be easily modified and run again for other file types,and can be easily modified to remove files after they have been converted:
convert_file() {
# loop through all mp4 files in current directory
for i in $1*.mp4;
# convert to .h264
do ffmpeg -i "$i" -b:v 10M -b:a 192k -an -vcodec libx264 -crf 23 "${i%.*}.h264" -y
done
# recursive call
# call convert_file function for each sub directory
for d in $1*/; do
if [ -d "$d" ]
then
convert_file $d;
fi
done
}
# call with path/to/myvideos
convert_file ~/Videos/
Related
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
I am working on a batch conversion process where I need to add a random picture (a .jpg) on a random .mp3 and make an .mp4.
This is the code I am using in terminal Mac OS X putting all the .mp3 and the .jpg in the same folder:
for f in *.mp3; do /usr/local/Cellar/ffmpeg/4.1_1/bin/ffmpeg -r 1 -loop 1 -i abc5.jpg -i "$f" -c:a copy -c:v libx264 -vf scale=1280:720 -shortest "${f%mp3}mp4"; done
It works great, but I don't know how to add another random feature for the .jpg files, so every time I have to put a different .jpg and change the name (usually in the file not in the script). I also know this must be something with for ((..., but I don't know how to add it properly.
for f in *.mp3; do /usr/local/Cellar/ffmpeg/4.1_1/bin/ffmpeg -r 1 -loop 1 -i abc5.jpg -i "$f" -c:a copy -c:v libx264 -vf scale=1280:720 -shortest "${f%mp3}mp4"; done
I'm using a script to convert all video files in a folder. These files have multiple audio tracks and I want the converted files to have each audio track as well. I've tried both -c copy and -c:a mp3 and neither worked for me. Any ideas how I can modify this to copy all audio tracks?
#!/bin/sh
for i in *.mkv; do ffmpeg -i "$i" -c:a mp3 -vcodec libx264 -crf 18 -r 60 "${i%.mkv}.mp4"; done
You can do this by setting -c:a copy. In your example you're attempting to set the audio codec to mp3, which is actually just the container. Hope that helps!
I should amend this... My first thought will simply copy the single, highest-quality audio stream from the input file. To copy all streams we'll want to use the map option1. Your example, copying all audio streams could look like this:
#!/bin/sh
for i in *.mkv; do
ffmpeg -i "$i" -map 0 -c copy -c:v libx264 -crf 18 -r 60 "${i%.mkv}.mp4";
done
How do I encode multiple video files in one batch with ffmpeg, using the same settings?
I found this one-line solution that converts .avi files in the current folder to .mov. Note that I want to encode .mov -> .mov :
for i in *.mov; do ffmpeg -i "$i" "${i%.mov}.mov" ; done
I wish to use the following settings for encoding:
ffmpeg -i "$i" -c:v libx265 -preset ultrafast -crf 20 -af "volume=25dB, highpass=f=200, equalizer=f=50:width_type=h:width=100:g=-15" -c:a aac -strict experimental -b:a 192k OUTPUT-ENCODED.MOV
Possible ways to prevent overwriting:
Add -ENCODED to the end of the filename before the file extension
Rename the files to something sequential, like OUTPUT01.MOV, OUTPUT02.MOV, etc
Put the encoded files in a directory subfolder but with the same file names
You can freely manipulate the output file ${i%.mov}.mov - here, the "key ingredient" is that the statement ${i%.mov} yields content of variable i with the shortest match of .mov deleted from the back. For details see this tutorial on manipulating strings in bash.
Does anyone know if it is possible to encode a video using ffmpeg in reverse? (So the resulting video plays in reverse?)
I think I can by generating images for each frame (so a folder of images labelled 1.jpg, 2.jpg etc), then write a script to change the image names, and then re-encode the ivdeo from these files.
Does anyone know of a quicker way?
This is an FLV video.
Thank you
No, it isn't possible using ffmpeg to encode a video in reverse without dumping it to images and then back again. There are a number of guides available online to show you how to do it, notably:
http://ubuntuforums.org/showthread.php?t=1353893
and
https://sites.google.com/site/linuxencoding/ffmpeg-tips
The latter of which follows:
Dump all video frames
$ ffmpeg -i input.mkv -an -qscale 1 %06d.jpg
Dump audio
$ ffmpeg -i input.mkv -vn -ac 2 audio.wav
Reverse audio
$ sox -V audio.wav backwards.wav reverse
Cat video frames in reverse order to FFmpeg as input
$ cat $(ls -r *jpg) | ffmpeg -f image2pipe -vcodec mjpeg -r 25 -i - -i backwards.wav -vcodec libx264 -vpre slow -crf 20 -threads 0 -acodec flac output.mkv
Use mencoder to deinterlace PAL dv and double the frame rate from 25 to 50, then pipe to FFmpeg.
$ mencoder input.dv -of rawvideo -ofps 50 -ovc raw -vf yadif=3,format=i420 -nosound -really-quiet -o - | ffmpeg -vsync 0 -f rawvideo -s 720x576 -r 50 -pix_fmt yuv420p -i - -vcodec libx264 -vpre slow -crf 20 -threads 0 video.mkv
I've created a script for this based on Andrew Stubbs' answer
https://gist.github.com/hfossli/6003302
Can be used like so
./ffmpeg_sox_reverse.sh -i Desktop/input.dv -o test.mp4
New Solution
A much simpler method exists now, simply use the command (adjusting input.mkv and reversed.mkv accordingly):
ffmpeg -i input.mkv -af areverse -vf reverse reversed.mkv
The -af areverse will reverse audio, and -vf reverse will reverse video. The video and audio will be in sync automatically in the output file reversed.mkv, no need to worry about the input frame rate or anything else.
On one video if I only specified the -vf reverse to reverse video (but not audio), the output file didn't play correctly in mkv format but did work if I changed it to mp4 output format (I don't think this use case of reversing video only but not audio is common, but if you do run into this issue you can try changing the output format). On large input videos that exceed the RAM available in your computer, this method may not work and you may need to chop up the input file or use the old solution below.
Old Solution
One issue is the frame rate can vary depending on the video, many answers depend on a specific frame rate (like "-r 25" for 25 frames per second). If the frame rate in the video is different, this will cause the reversed audio and video to go out of sync.
You can of course manually adjust the frame rate each time (you can get the frame rate by running ffmpeg -i video.mkv and look for the number in front of the fps, this is sometimes a decimal number like 23.98). But with some bash code you can easily extract the fps, store it in a variable, and automatically pass it to the programs.
Based on this I've created the following bash script to do that. Simply chmod +x it and run it ./make-reversed-video.sh input.mkv output.mkv. The code is as follows:
#!/bin/bash
#Partially based on https://nhs.io/reverse/, but with some modifications, including automatic extraction of the frame rate.
#Get parameters.
VIDEO_FILE=$1
OUTPUT_FILE=$2
TEMP_FOLDER=$3
echo Using input file: $VIDEO_FILE
echo Using output file: $OUTPUT_FILE
mkdir /tmp/create_reversed_video
#Get frame rate.
FRAME_RATE=$(ffmpeg -i "$VIDEO_FILE" 2>&1 | grep -o -P '[0-9\\. ]+fps' | grep -o -P '[0-9\\.]+')
echo The frame rate is: $FRAME_RATE
#Extract audio from video.
ffmpeg -i "$VIDEO_FILE" -vn -ac 2 /tmp/create_reversed_video/audio.wav
#Reverse the audio.
sox -V /tmp/create_reversed_video/audio.wav /tmp/create_reversed_video/backwards.wav reverse
#Extract each video frame as an image.
ffmpeg -i "$VIDEO_FILE" -an -qscale 1 /tmp/create_reversed_video/%06d.jpg
#Recombine into reversed video.
ls -1 /tmp/create_reversed_video/*.jpg | sort -r | xargs cat | ffmpeg -framerate $FRAME_RATE -f image2pipe -i - -i /tmp/create_reversed_video/backwards.wav "$OUTPUT_FILE"
#Delete temporary files.
rm -rf /tmp/create_reversed_video
I've tested it and it works well on my Ubuntu 18.04 machine on lots of videos (after installing the dependencies like sox). Please let me know if it works on other Linux distributions and versions.