LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 04-14-2009, 07:19 AM   #1
kDest
LQ Newbie
 
Registered: Apr 2009
Posts: 5

Rep: Reputation: 1
A Personal BASH FFmpeg Batch Script (WIP)


I have been working, when time allows, on a BASH script for batch video conversion using FFmpeg. The story is basically that I have a large number of video files which needed standardised formatting (Matroska, similar framerates, dimensions, quality, tagging, etc.) and I was using TMPEGEnc to encode them but decided to move to FFmpeg after VMware virtualisation seemed to desync the audio.

So basically, I made a single-file transcode script which works perfectly, however I want to make it batch. I think an idea solution would be a script which executes and asks a few questions which you fill out resolution, quality, directory, target directory, etc. However, I'm fairly inexperienced with BASH syntax and scripting so I thought I'd ask here what I'm doing wrong, and any pointers on how to prompt via BASH for variable input, as well as enumerating a directory with spaces in the path using a filter (which I *thought* I figured out how to do), and perhaps string manipulation (to handle file renaming, and such).

Here's what I have so far:

Code:
#!/bin/sh

input=1
audioquality=2
videoquality=3
size=4
fps=5

ls $1 | while read file; do
	echo $file
        ffmpeg -i "$file" -acodec vorbis -aq $2 -ac 2 -ar 48000 -vcodec libx264 -crf $3 -s $4 -r $5 -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 -f matroska "$file"\.ffmpeg
	mkvmerge -o "$file"\.mkv --language 1:eng --track-name "1:FFmpeg x264, CRF=$3, $4, $5 fps" --language 2:eng --track-name "2:FFmpeg Vorbis, Q=$2, 48000 Hz, Stereo" -a 2 -d 1 -S "$file"\.ffmpeg --track-order 0:1,0:2
done
Ideally I'd replace certain hard-coded parameters with user input, but right now the big problem is making this loop correctly. It just works for one file then quits, which seems strange, since I borrowed the framework for the loop from a search on the internet which should have worked.

I'll post the final work when I get this functional, it should be more useful than a number of the frontends I've seen on Linux and Windows for FFmpeg.
 
Old 04-14-2009, 08:13 AM   #2
bgeddy
Senior Member
 
Registered: Sep 2006
Location: Liverpool - England
Distribution: slackware64 13.37 and -current, Dragonfly BSD
Posts: 1,810

Rep: Reputation: 231Reputation: 231Reputation: 231
Just a quick suggestion - try this little change :
Code:
ls "$@" | while read file; do
	echo "$file"
in your loop.
 
Old 04-14-2009, 12:28 PM   #3
Guttorm
Senior Member
 
Registered: Dec 2003
Location: Trondheim, Norway
Distribution: Debian and Ubuntu
Posts: 1,317

Rep: Reputation: 350Reputation: 350Reputation: 350Reputation: 350
Hi

It might also be that the commands reads from standard input or does something else with it. Consider a script like this:
Code:
ls $1 | while read file ; do
        echo "Filename: $file"
        read whattodo
        echo "You typed: $whattodo"
done
This will not work as expected. Instead of reading from the keyboard, the variable gets the next file.

Code:
for file in $(ls $1) ; do
        echo "Filename: $file"
        read whattodo
        echo "You typed: $whattodo"
done
Here the ls command is done first, and it reads from the keyboard.
 
Old 04-16-2009, 03:21 AM   #4
kDest
LQ Newbie
 
Registered: Apr 2009
Posts: 5

Original Poster
Rep: Reputation: 1
Sorry for the late reply, I had a bunch of school-related things that took up my time.

I didn't try:
Code:
ls "$@" | while read file; do
	echo "$file"
Because I wasn't sure how "$@" resolves into a variable for reference.

However, thanks to Guttorm's suggestions, I got this working:

Code:
#!/bin/sh

echo	"Directory, filters accepted: [Default $HOME]"
read	fs_dir
echo	"Audio Quality, 0-100 [Default 20]"
read	aud_qual
echo	"Video Constant Rate Factor, 0-52 [Default 26]"
read	vid_qual
echo	"Video Resolution, WIDTHxHEIGHT [Default 640x480]"
read	vid_res
echo	"Frames Per Second, 1-60 [Default 23.976]"
read	vid_fps

for file in $(ls $fs_dir) ; do
	echo "Now Reading: $file"
	echo "Audio Quality $aud_qual, Rate 48000 hz"
	echo "Video Quality $vid_qual, Size $vid_res, Rate $vid_fps fps"
	ffmpeg -i "$file" -acodec vorbis -aq "$aud_qual" -ac 2 -ar 48000 -vcodec libx264 -crf "$vid_qual" -s "$vid_res" -r "$vid_fps" -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 -f matroska "$file"\.ffmpeg
	mkvmerge -o "$file"\.mkv --language 1:eng --track-name "1:FFmpeg x264, CRF=$vid_qual, $vid_res, $vid_fps fps" --language 2:eng --track-name "2:FFmpeg Vorbis, Q=$aud_qual, 48000 Hz, Stereo" -a 2 -d 1 -S "$file"\.ffmpeg --track-order 0:1,0:2
	echo "Now Deleting Temporary File: $file"
	rm "$file"\.ffmpeg
	echo "Now Finished: $file\.mkv"
done
I'll probably add the conditionals next. I'll give a status update then, in case anyone wants to reuse the script.
 
Old 04-16-2009, 12:47 PM   #5
gnashley
Amigo developer
 
Registered: Dec 2003
Location: Germany
Distribution: Slackware
Posts: 4,891

Rep: Reputation: 569Reputation: 569Reputation: 569Reputation: 569Reputation: 569Reputation: 569
Use 'ls -1 $1' for better looping -to print the result of ls in a single column. Otherwise you should set the IFS -normal output of ls uses tabs as separators.
 
Old 04-17-2009, 03:20 AM   #6
kDest
LQ Newbie
 
Registered: Apr 2009
Posts: 5

Original Poster
Rep: Reputation: 1
Quote:
Use 'ls -1 $1' for better looping -to print the result of ls in a single column. Otherwise you should set the IFS -normal output of ls uses tabs as separators.
Can you elaborate on that? My understanding is the $<number> is the passed parameter to the script, which is great but it would seem that spaces in the $<number> are not passed correctly, even when passed within apostrophes or even if in the script the $<number> is in apostrophes.

Does passing -l to ls alleviate this problem? Because presently I discovered that the script still cannot handle spaces (it's infuriating that a feature rich OS like Linux has a command interpreter, BASH, which can't deal with spaces easily).
 
Old 04-17-2009, 11:20 AM   #7
gnashley
Amigo developer
 
Registered: Dec 2003
Location: Germany
Distribution: Slackware
Posts: 4,891

Rep: Reputation: 569Reputation: 569Reputation: 569Reputation: 569Reputation: 569Reputation: 569
That's the digit '1', not an el. It just makes sure that the output of ls is a single column with each item followed by a line break. Otherwise the 'while' loop uses the default IFS which is <space><tab><linebreak>. You can also do this:
Code:
OLD_IFS=$IFS
IFS='
'
for file in $(ls $fs_dir) ; do
	code...
done
IFS=$OLD_IFS
to separate items. You can also use 'find' instead of ls.
 
Old 04-19-2009, 01:18 PM   #8
RandiR
LQ Newbie
 
Registered: Mar 2009
Posts: 6

Rep: Reputation: 1
FFmpeg Batch Script to convert video files

Here is an alternate script to do the same using biterscripting (http://www.biterscripting.com - it is free).

Code:
# Input arguments
var str audioquality, videoquality, size, fps
var str filelist
# Collect the list of files
ls -rn "*" > $filelist
while ($filelist <> "")
do
        # Get the next file
        var str file ; lex "1" $filelist > $file
        system ffmpeg "-i" $file "-acodec vorbis -aq" $audioquality "-ac 2 -ar 48000 -vcodec libx264 -crf" $videoquality "-s" $size "-r" $fps "-flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 -f matroska" ($file+".ffmpeg")
	system  mkvmerge -o ($file+".mkv") "--language 1:eng --track-name "1:FFmpeg x264, CRF=$3, $4, $5 fps" --language 2:eng --track-name "2:FFmpeg Vorbis, Q=$2, 48000 Hz, Stereo" -a 2 -d 1 -S" ($file+".ffmpeg") "--track-order 0:1,0:2"
done
Store this script in someplace such as C:/Scripts/myscript.txt. Call the script as follows in biterscripting.
Code:
script myscript.txt audioquality("value") videoquality("value") size("value") fps("value")
Use appropriate "value"s. Any variables declared at the main level of a script, of any type, can be assigned a value this way, in any order.


Randir Randwa
 
Old 04-24-2009, 07:01 AM   #9
wreckgar23
LQ Newbie
 
Registered: Apr 2009
Posts: 9

Rep: Reputation: 0
useful thread for bash ffmpeg scripts

I wasn't able to use the solution suggested here. I think that because the name of the directory I was searching for films contained whitespaces, I had to use a while loop ie.

find "/media/Local Disk/Documents and Settings/Some Pictures" -iname *.mov | while read file

If I used ffmpeg within the while loop, it appeared to work for alternate passes of the loop. It looked like it'd read one file name and then an additional 1024 characters of the find output for each iteration. Most probably for the reasons described in this thread.

Anyway, another suggested solution (which worked for me) is to use I/O redirection with ffmpeg like so

ffmpeg -i "$file" -r pal -target vcd $output >/dev/null 2>/dev/null </dev/null

I can't actually post the link to the other posting on this forum that gave me that solution because this is my first post!
 
Old 04-24-2009, 11:11 AM   #10
RandiR
LQ Newbie
 
Registered: Mar 2009
Posts: 6

Rep: Reputation: 1
Directory with spaces

If the directory has spaces, use double quotes around it.

Also, add the following command to the beginning of my biterscript.

cd "/media/Local Disk/Documents and Settings/Some Pictures"

Use the correct name of the directory where the pictures are located.

Randir Randwa
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Batch convert video in entire folder using ffmpeg Steven Hyde Linux - General 4 05-03-2008 06:28 PM
How to run a BASH script in a Batch file (with Cygwin) FaeDine Programming 2 10-27-2007 05:47 PM
need a bash script to batch convert .wav to .mp3 nass Slackware 15 06-23-2007 02:00 AM
FFMPEG vs MPLAYER vs MENCODER and batch conversions to MP4 ? AngusM Linux - Software 2 11-08-2006 09:20 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 06:48 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration