I want to reduce the quality of a bunch of images at the time. With -q:v x where x is a number between 1 and 30 (the bigger the number, the worse the quality). I'm able to save a lot of space even with x=1. Now, when it comes to process multiple files, I'm stuck.
I've tried these two batch files:
mkdir processed
for f in *.jpg;
do name=`echo $i | cut -d'.' -f1`;
echo $name;
ffmpeg -i $i -q:v 1 processed/$name.jpg;
done
And
mkdir processed
for f in *.jpg;
do ffmpeg -i "$f" -q:v 1 processed/"${f%.jpg}.jpg";
done
Both just create the processed folder but nothing else.
Thanks to #Squashman for pointing out my stupid mistake. This is my solution to the problem for Windows batch script.
mkdir processed
for %%F in (*.jpg) do (
ffmpeg -i "%%F" -q:v 10 "processed\%%F"
)
I got like a 80% of weight reduction.
Here is a solution, using ffmpeg in a python script. More verbose, but therefore easier to read.
from pathlib import Path
import os
suffix = ".jpg"
input_path= Path.home() / "Downloads"
file_paths= [subp for subp in input_path.rglob('*') if suffix == subp.suffix]
file_paths.sort()
output_path = Path.home() / "Downloads/processed"
output_path.mkdir(parents=True, exist_ok=True)
for file_p in file_paths:
input = str(file_p)
output = str( output_path / file_p.name )
command = f"ffmpeg -i {input} -q:v 10 {output}"
os.system(command)
for linux you can make this script
if all your images are in this directory images
make a script.sh which contains the following code
mkdir images/processed
for f in images/*.jpg images/*.png images/*.jpeg
do
ffmpeg -i $f -q:v 10 processed/$f -y
done
Related
I'm a C programmer on linux.
I write a program that saves an image in /srv/ftp/preview.png which is updating frequently and i want to create a movie from this updates.
It's timestamp is important for me, e.g if image updates after 3.654 seconds i want movie show this update(frame) after 3.654 seconds too.
I searched in Internet for several hours but i can't find any solution.
I know about ffmpeg but it will convert images(and not one image) to movie without millisecond timestamp.
I found this Question but it seems is not useful in this case.
Is there any tool to do that? if not, please introduce an API in c to write a program myself
You can try to use inotify watch modification on the file and ffmpeg to append file to the movie:
#!/bin/bash
FRAMERATE=1
FILE="/path/to/image.jgp"
while true
do inotifywait -e modify "$FILE"
echo "file changed"
# create temp file name
TMP=$(mktemp)
# copy file
cp "$FILE" "$TMP$
# append copy file to movie
# from https://video.stackexchange.com/q/17228
# if movie already exist
if [ -f movie.mp4 ]
then
# append image to a new movie
ffmpeg -y -i movie.avi -loop 1 -f image2 -t $FRAMERATE -i "$TMP".jpg -f lavfi -t 3 -i anullsrc -filter_complex "[0:v] [1:v] concat=n=2:v=1 [v] " -map "[v]" newmovie.avi
# replace old by new movie
mv newmovie.mp4 movie.mp4
else
#create a movie from one image
ffmpeg -framerate 1 -t $FRAMERATE -i "$TMP" movie.mp4
fi
rm "$TMP"
done
This script must certainly be adapted, (in particular if your framerate is high) but I think you can try to play with it.
One bad thing also that the movie creation will become slower and slower because the movie becomes bigger.
You should to store images of a certain time duration in a directory and convert all at once (like once an hour/day)
If you want to serve a stream instead of creating a video file, you can look at https://stackoverflow.com/a/31705978/1212012
So easy and cool batch question. Sorry for this ultimate newbie question !
I've a folder which contains hundreds of videos like that:
Video001 - Introduction.avi
Video002 - History of Stack Overflow.avi
Video003 - Before Asking.avi
...
Video999 - Conclusion.avi
I need re-encode all of them with x264 codec. Ffmpeg is very slow so I tried to use HandBrake. There is a command line edition of HB and great default presents. I could write this command for converting only one file with "Normal" present:
HandBrakeCLI.exe -i "Video001 - Introduction.avi" -o "Video001 - Introduction.mp4" -Z Normal
My question: How can I convert all of them in folder and delete after conversion process? Thank you!
Note: If you think ffmpeg is better solution I can give my fav present. Handbrake says about Normal present:
Normal: -e x264 -q 20.0 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 --loose-anamorphic --modulus 2 -m --x264-preset veryfast --h264-profile main --h264-level 4.0
for %%f in (*.avi) do (
handbrakecli.exe -i "%%~nxf" -o "%%~nf.mp4" -Z Normal
del "%%~f"
)
For each avi file, call handbrake with the name and extension of input file and generate an output file with the name of the original file and .mp4 extension
I need to get a list of files added to a master folder and copy only the new files to the respective backup folders; The paths to each folder have multiple folders, all named by numbers and only 1 level deep.
ie /tester/a/100
/tester/a/101 ...
diff -r returns typically "Only in /testing/a/101: 2093_thumb.png" per line in the diff.txt file generated.
NOTE: there is a space after the colon
I need to get the 101 from the path and filename into separate variables and copy them to the backup folders.
I need to get the lesserfolder var to get 101 without the colon
and mainfile var to get 2093_thumb.png from each line of the diff.txt and do the for loop but I can't seem to get the $file to behave. Each time I try testing to echo the variables I get all the wrong results.
#!/bin/bash
diff_file=/tester/diff.txt
mainfolder=/testing/a
bacfolder= /testing/b
diff -r $mainfolder $bacfolder > $diff_file
LIST=`cat $diff_file`
for file in $LIST
do
maindir=$file[3]
lesserfolder=
mainfile=$file[4]
# cp $mainfolder/$lesserFolder/$mainfile $bacfolder/$lesserFolder/$mainfile
echo $maindir $mainfile $lesserfolder
done
If I could just get the echo statement working the cp would work then too.
I believe this is what you want:
#!/bin/bash
diff_file=/tester/diff.txt
mainfolder=/testing/a
bacfolder= /testing/b
diff -r -q $mainfolder $bacfolder | egrep "^Only in ${mainfolder}" | awk '{print $3,$4}' > $diff_file
cat ${diff_file} | while read foldercolon mainfile ; do
folderpath=${foldercolon%:}
lesserFolder=${folderpath#${mainfolder}/}
cp $mainfolder/$lesserFolder/$mainfile $bacfolder/$lesserFolder/$mainfile
done
But it is much more reliable (and much easier!) to use rsync for this kind of backup. For example:
rsync -a /testing/a/* /testing/b/
You could try a while read loop
diff -r $mainfolder $bacfolder | while read dummy dummy dir file; do
echo $dir $file
done
I'm using ffmpeg to extract the audio from different video formats (flv, mp4) and convert it to mp3.
%~dp0ffmpeg.exe -i %1 -ar 44100 -ac 2 -ab 128k "%~dpn1.mp3"
This works just fine. However, in my input files, the audio bitrate varies, and I want to adjust the output bitrate accordingly. Even by extensive Google searching, I didn't find any hint how to just keep the original bitrate.
What I would need would be something like:
-ab copy
Which, of course, does not work.
Is there anything that will work?
P.S: As you might have figured from the formatting above, I'm using a windows batch file. There would be the hack to use %~dp0ffmpeg.exe -i, get the audio bitrate by grep and insert it in the command line. I just think there has to be an easier and more elegant way.
even though the original thread was looking for an answer without grepping anything, nate's script seems to be the most useful post. but it has some limitations, for example not all outputs give you a bitrate grepped, some turnout to give you just the result "default". here's a little more improved version of it.
#!/bin/env bash
ext=$1
for f in *.${ext}; do
x=${f%.*} ;
x=${x% - YouTube}; # I usually download some song covers from YouTube.
x=$x".mp3";
bit=`ffmpeg -i "${f}" 2>&1 | grep Audio | awk -F", " '{print $5}' | cut -d' ' -f1`
if [ -n "$bit" ]; then
ffmpeg -i "$f" -ab ${bit}k "$x"
else
ffmpeg -i "$f" "$x" # this is if we don't have an output from the grep line above, ffmpeg will keep the original quality, that is 192k for 192k
fi
done
Here is a bash script that will take a file extension and extract audio from any file with that extension, and of course maintain the bitrate. I can't claim the credit of the key piece of the code, as that goes to the gentleman that writes this blog.
#!/bin/bash
ext=$1
for file in *.${ext}; do
tmpfn=${file%.*} ; # get rid of file ext
tmpfn=$tmpfn".mp3"; # add mp3 file ext
# next line gets bitrate of audio from video using ffmpeg
bit=`ffmpeg -i "${file}" 2>&1 | grep Audio | awk -F", " '{print $5}' | cut -d' ' -f1`
# finally, convert to mp3 using proper bitrate
ffmpeg -i "$file" -ab ${bit}k "$tmpfn"
done
Just run it in the directory where you have the files like so:
$bash script.sh flv
where flv is the file extension. Hack it to make it do exactly as you wish or process multiple filetypes to your heart's content.
EDIT: Just a quick note for anyone on Ubuntu/debian/etc. Make sure you install the additional codec package or else it won't work, i.e. you must install ffmpeg and the extra libav codec package or you're gonna have a bad time. This should do the trick:
sudo apt-get ffmpeg libavcodec-extra-53
As LordNeckbeard states, using the same bitrate to encode in different formats isn't necessarily wise. However...
Here is a batch-file solution which captures the input file bitrate and uses that as a parameter for the encoding command line. This approach was hinted at by the original questioner. The mp3 output file is created in the same folder as the input file.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM Usage: scriptname.cmd "full-quoted-path-to-input-file"
REM Adjust FFMPEG_PATH variable value to match the path to your FFMPEG binary
SET FFMPEG_PATH=C:\Program Files\ffmpeg-20170807-1bef008-win64-static\bin
SET INPUT_FILE_FULL_PATH=%1
REM Get input file bitrate
FOR /F "tokens=5 delims==," %%i IN ('""%FFMPEG_PATH%\ffmpeg.exe" -i %INPUT_FILE_FULL_PATH% 2>&1 | find "Audio:""') DO (
FOR /F "tokens=1 delims==k" %%j IN ('ECHO %%i') DO (
SET BITRATE=%%j
SET BITRATE=!BITRATE: =!
ECHO Input file bitrate is !BITRATE! kb/s
)
)
REM Encode file using previously captured bitrate
"%FFMPEG_PATH%\ffmpeg.exe" -i %INPUT_FILE_FULL_PATH% -ar 44100 -ac 2 -ab !BITRATE!k "%~dpn1.mp3"
There is scope for tightening the code up, for example, a check to make sure at least one argument was provided, and that the BITRATE is not empty before beginning the encode, but as a rough and ready solution this should do fine.
Current version of ffmpeg (tested 2.1.4) recommends using "-qscale 0" to preserve quality. This worked for me on my mpeg4 video test file.
Try this option: -codec copy or -acodec copy for only audio.
Check this reference http://ffmpeg.org/ffmpeg.html#Stream-specifiers-1
Instead of
-ab copy
try
-sameq
%~dp0ffmpeg.exe -i %1 -sameq "%~dpn1.mp3"
I execute the following command line
ffmpeg.exe
-i C:\Beema\video-source\DO_U_BEEMA176x144short.avi
-i C:\Beema\video-source\DO_U_BEEMA176x144short.avi
-i C:\Beema\temp\9016730-51056331-stitcheds.avi
-i C:\Beema\video-source\GOTTA_BEEMA176x144short.avi
-y -ac 1 -r 24 -b 25K
C:\Beema\video-out\9a062fb6-d448-48fe-b006-a85d51adf8a1.mpg
The output file in video-out ends up having a single copy of DO_U_BEEMA. I do not understand why ffmpeg is not concatenating.
Any help is dramatically appreciated,
mencoder -oac copy -ovc copy file1 file2 file3 … -o final_movie.mpg
Have you tried with mencoder?
Also are all of the files the same bitrate and dimensions? If not your going to need to make sure all of the video files are identical in these two areas before attempting to combine. It also appears your attempting to combine .avi's with a single .mpg, you'll most likely want to convert the .mpg to a similar format when re-encoding.
Hope this helps.
If it has C:\, it's Windows. use:
copy video1 + video2 + video3
and add more + videoN instances until you get there.
Here is the command that will work for you first cat the files then pipe to ffmpeg
cat C:\Beema\video-source\DO_U_BEEMA176x144short.avi C:\Beema\video-source\DO_U_BEEMA176x144short.avi C:\Beema\temp\9016730-51056331-stitcheds.avi
C:\Beema\video-source\GOTTA_BEEMA176x144short.avi | ffmpeg -f mpeg -i - C:\Beema\video-out\9a062fb6-d448-48fe-b006-a85d51adf8a1.mpg
-i - is important "-" this is the piped input to ffmpeg
Cheers.
I guess ffmpeg cann't do that. It usually takes only one input file. Trying to cat files to input may result in a lump togather, but i guess it won't stitch properly.
Best bet is mencoder or using transcode