ffmpeg - set variable video duration - bash

I use this command to convert files in batch plus crop and re-scale:
for i in $( ls *.mp4 ); do
ffmpeg -y -i "$i" -acodec libfaac -ab 128k -ar 44100 -vcodec libx264 -b 850k -threads 0 -vf [in]crop=in_w-112:in_h-63:56:0,scale=1280:720[out] "../../archive/${i/.mp4/}.mp4"
done
this command will start at second 15, and makes video 30 seconds long:
for i in $( ls *.mp4 ); do
ffmpeg -ss 00:00:15 -t 30 -y -i "$i" -acodec libfaac -ab 128k -ar 44100 -vcodec libx264 -b 850k -threads 0 -vf [in]crop=in_w-112:in_h-63:56:0,scale=1280:720[out] "${i/.mp4/}_test.mp4"
done
what I would like is a command that cuts off 15s from the beginning and 15s from the end of EACH video from the BATCH ... the trick is that each video has different duration, so "how long it is" must be a variable (duration minus 15s or minus 30s if I count 15s from the start as well)
video duration examples:
video 1 - 00:25:19
video 2 - 00:15:34
video 3 - 00:19:21
video 4 - 00:22:49
etc.

Couldn't you do this with a simple bash script?
As stated here this command will retrieve the video duration in seconds:
ffprobe -i some_video -show_entries format=duration -v quiet -of csv="p=0"
So you can read that into a variable, then simply withdraw 30 seconds and you will now what duration to set in your ffmpeg command.
So, step 1 will be the first for-loop you have already posted in your question.
Step 2 will be to withdraw 30 seconds from the movies duration and save that in a variable.
Then in step 3, rewrite your second for-loop like this:
for i in $( ls *.mp4 ); do
ffmpeg -ss 00:00:15 -t $your_new_duration_variable -y -i "$i" -acodec libfaac -ab 128k -ar 44100 -vcodec libx264 -b 850k -threads 0 -vf [in]crop=in_w-112:in_h-63:56:0,scale=1280:720[out] "${i/.mp4/}_test.mp4"
done

Related

FFMPEG: Encoding WEBM with fast-seek and copyts leads to wrong video length

I'm trying to convert a scene from a soft-subbed MKV file into a hard-subbed WEBM file with two-pass. The video encodes fine, but the file shows the wrong length when opened on a media player (it's showing as if I had encoded the original file from the starting point all the way to the end).
This is the command I'm using:
set timestamp=-ss 12:59.069 -to 16:14.277
ffmpeg -y %timestamp% -copyts -i source.mkv -shortest -c:v libvpx-vp9 -pass 1 -b:v 0 -crf 33 -threads 8 -speed 4 -tile-columns 6 -frame-parallel 1 -an -sn -vf scale=-1:720,subtitles=source.mkv -f webm NUL
ffmpeg -y %timestamp% -copyts -i source.mkv -shortest -c:v libvpx-vp9 -pass 2 -b:v 0 -crf 33 -threads 8 -speed 2 -tile-columns 6 -frame-parallel 1 -auto-alt-ref 1 -lag-in-frames 25 -c:a libopus -b:a 64k -sn -vf scale=-1:720,subtitles=source.mkv -f webm out.webm
When opening the video in MPC-BE, the video plays regularly until around the point shown on https://i.stack.imgur.com/6bRwc.png (which is where the scene I wanted to cut out ends) then it just skips to the end of the file, and this wrong length is giving me all sorts of issues when I try to use the encoded video.
Apparently, your player doesn't like a non-zero starting timestamp (at least in WebMs).
So, reset the timestamps before writing (I assume you're using copyts for the subtitles filter alignment).
In pass 2,
ffmpeg -y %timestamp% -copyts -i source.mkv -shortest -c:v libvpx-vp9 -pass 2 -b:v 0 -crf 33 -threads 8 -speed 2 -tile-columns 6 -frame-parallel 1 -auto-alt-ref 1 -lag-in-frames 25 -c:a libopus -b:a 64k -sn -vf scale=-1:720,subtitles=source.mkv -output_ts_offset -12:59.069 -f webm out.web
where the value for ts offset is the negative of your ss value.

Is it possible to split frame accurate an AAC file using FFmpeg?

What I did:
# create source material
ffmpeg -y -i some.file -c:a libfdk_aac -profile:a aac_he -b:a 128k -ar 44100 source.m4a
# split into two parts
ffmpeg -y -ss 00:00:00 -i source.m4a -to 6 -c copy part1.m4a
ffmpeg -y -ss 00:00:06 -i source.m4a -c copy part2.m4a
# re-encode only the first part with the same setting as source file
fmpeg -y -i part1.m4a -c:a libfdk_aac -profile:a aac_he -b:a 128k -ar 44100 part1reencoded.m4a
# create file list to be concatenated
echo 'ffconcat version 1.0
file part1reencoded.m4a
file part2.m4a' > my.list
# finally concatenate both parts
ffmpeg -y -f concat -safe 0 -i my.list -c copy parts.m4a
# play the result
ffplay parts.m4a
Unfortunately, the result file has noises at 00:00:06.
Is it possible to split frame accurate an AAC file using FFmpeg?
You can split frame accurately, at 44100, every frame is 23 milliseconds.
What you can’t do is concatenate aac from different encodes.

Using FFMPEG concat to produce a short output video

I need to concatenate 306 images using FFMPEG.
so, I proceed with the following script shell :
touch input.txt
for i in `seq 0 305`;
do
echo "file $i.png" >> input.txt
echo "duration 1" >> input.txt
done
echo "file 305.png" >> input.txt
ffmpeg -f concat -i input.txt -vf fps=10 -vsync vfr -pix_fmt yuv420p video.mp4
I can't find the right parameters for concat.
I searching about duration, I can't find if it's possible to specify
less than 1 second.
Every time, I change fps, the duration of output video became so
big; example: if fps = 10 , output video duration = 3060 seconds
also, I tried :
1. ffmpeg -f concat -i input.txt -vf fps=10 -vsync vfr -pix_fmt yuv420p
video.mp4
2. ffmpeg -f concat -i input.txt -y -vf fps=1 -crf 22 -threads 2
-preset veryfast video.mp4
I tried also, with duration 1 for all images, the final video show only 1 image.
I need to concat those images and produce a short output video.
any idea

ffmpeg two-pass in ts stream production

How can I make a two-pass convert while using .ts chunks output?
I use the following command inside bash script for chunks generation (I think all variables are clear enough for understanding):
ffmpeg -i $1 -threads 1 -b:v ${selected_bitrate} -b:a ${audio_bitrate} -s ${selected_width}x${selected_height} -r ${framerate} -preset fast -level ${level} -vcodec libx264 -f ssegment -segment_list b${selected_bitrate}.m3u8 -segment_time 9 -force_key_frames "expr:gte(t,n_forced*9)" -y b${selected_bitrate}_%05d.ts
I want to try two-pass because I need to match desired bitrate more accurate. Right now when I use for example 200k bitrate for video stream, it results ~380k in ts chunks (of course without audio).
Just call the libx264 with "-pass 1" like the following:
ffmpeg -i $1 -threads 1 -ar -b:v ${selected_bitrate} -s ${selected_width}x${selected_height} -r ${framerate} -preset fast -level ${level} -vcodec libx264 -pass 1 -f null -
Then repeat your command with "-pass 2":
ffmpeg -i $1 -threads 1 -b:v ${selected_bitrate} -b:a ${audio_bitrate} -s ${selected_width}x${selected_height} -r ${framerate} -preset fast -level ${level} -vcodec libx264 -pass 2 -f ssegment -segment_list b${selected_bitrate}.m3u8 -segment_time 9 -force_key_frames "expr:gte(t,n_forced*9)" -y b${selected_bitrate}_%05d.ts
That should give you what you want.

Launch a file script after the first one is done [duplicate]

This question already has answers here:
bash script order of execution
(2 answers)
Closed 7 years ago.
I will be on 3 days vacation, so I would like to do a task in one file, after it's done, launch the other one using bash script, the way I would like to do is:
List files location in one file, i.e (toDo.txt)
Once the first file is done it goes to the other one.
Example:
doDo.txt contents:
/home/me/www/some_dir/file1F42.sh
/home/me/www/another_dir/fileD2cD.sh
/home/me/www/third_dir/fileG0IU.sh
/home/me/www/last_dir/fileVFpO.sh
file1F42.sh:
some commands here
Once is done, it should jump to line 2, which is: fileD2cD.sh
I do NOT want to use cron, because I do not know when the files will finish treatment, and at the same time I do NOT want to launch all of them at once.
This is a real example that I just finish to do:
ffmpeg -i Original/$domainName"_"$fileName"_"$f-Original.mp4 -strict experimental -vf "drawtext=fontfile='/usr/share/fonts/truetype/freefont/FreeSansBold.ttf':text='www.alfirdaous.com':x="$SizeX":y="$SizeY":fontsize="$textSize":fontcolor=$textColor" -vcodec libx264 -preset medium -crf 24 -acodec copy Done/"$domainName"_"$fileName"_$f-Done.mp4
mp4Box=$(MP4Box -add Done/"$domainName"_"$fileName"_$f-Done.mp4 "$domainName"_"$fileName"_$f.mp4)
echo $mp4Box >> ffmpeg_exec.log;
# Delete Done files
rm Done/"$domainName"_"$fileName"_$f-Done.mp4
# Get master thumbnail
ffmpeg -itsoffset -150 -i "$domainName"_"$fileName"_$f.mp4 -vcodec mjpeg -vframes 1 -an -f rawvideo -s 640x480 "$silsilaName"_$f.png
n=0
for offset in 140 160 180 200 220 240 260 280 300 320
do
printf -v outfile "$silsilaName"_"$f"_"%03d.png" "$((++n))"
ffmpeg -itsoffset -$offset -i $domainName"_"$fileName"_"$f.mp4 -vcodec mjpeg -vframes 1 -an -f rawvideo -s 640x480 "$outfile"
done
ffmpeg -i "$domainName"_"$fileName"_$f.mp4 -vn -ar 44100 -ac 2 -ab 128 -f mp3 $f.mp3
done
Last command line is:
ffmpeg -i "$domainName""$fileName"$f.mp4 -vn -ar 44100 -ac 2 -ab 128 -f mp3 $f.mp3
How do I know that it is DONE, finish, so I can go to my file list "doDo.txt" and start running the next file?
#! /bin/sh
while read file; do
echo "Executing $file"
sh "$file"
done < /dev/stdin
Where usage is <script.sh> < file_list

Resources