[Home]
Table of contents
The creation of a video proceeds in three stages. The first is
the preprocessing, where I create the props (images and
animations that will be overlaid on top of the main video).
The shooting is done in the second stage. The third stage puts
everything together.
Other than the overall planning of the topic, the main effort in
this stage is devoted to creating the props: images and
animations. The animations themselves start out their lives as a
sequence of images. All the images (whether part of such a
sequnce or not) are finally produced by inkscape. Ocassionally,
I need to import supporting materials created by Art Of Illusion
or R. The steps are:
- Issue the command "domakelec <n>" to start a folder
called
lec<n>
. Henceforth I shall call it the
lecture folder. This folder contains an empty
subfolder called vid, and a template kdenlive file containing a
template title page, end page and the jingles.
- For each topic to be covered in the video, I create a folder
to hold all the materials needed for it. With each such folder
is associated a filename prefix (e.g., p). Then the files
p1.svg, p2.svg, etc are the images to be displayed in that
order. The folder may contain other svg files as well, but they
will not be displayed. If need animations, then the image
sequence is also placed in this folder (but with some other
prefix).
- If I plan to have some animation, the image files must be
stored in <png root><n>.png format, where <n> is a 4-digit 0-padded counter starting from 1. Then I
create the first svg file containing (linking, not embedding) the
first of these images. The svg file must be named
like <svg root><unpadded n>.svg. Then issuing the command
"doanim <svg root> <png root> <nframe>" creates the other svg files.
- Once all the folders are prepared, I create a file called
order.txt in the lecture folder that contains the folder names
followed by the associated prefixes like this:
intro,i
fd,f
lm,lm
bon,b
bon2,bb
lsd,lsd
Each line is a comma-separated pair, the first part being the name
of a folder and the second part its associated prefix.
- Next I issue the command "ipall y" in the lecture folder. This
converts all the prefixed svg files in the folders to
corresponding png files and also constructs index.txt files in
each folder with the names of the prefixed png files listed
alphabetically. It is the order of the names in the index.txt
file that determined the final order in which the images will
appear during the shooting.
- If we are using animation then the last frame of the
animation is included in the index.txt by hand.
- If I now issue the command 'doandro <folder>' from the
lecture folder, I can
preview the images in the shooting order. This is just for
checking. If any image needs changing, the command "ipall"
(without the "y") will create png from the updated
svg files bt not modify/create the index.txt files.
- When I am satisfied, I give the command "dotransflip". This
creates a folder called "transfer" under the lecture folder, and
creates folders named 1, 2, 3 etc storing the flipped versions
of the prefixed png images from the folders (according the order
specified by order.txt). The index.txt file in each folder is
copied, as well.
- Finally, I transfer the "transfer" folder to my mobile.
For shooting I start my Overlay app, select the folder, start the
camera, do OK syncing, and start speaking. Holding the camera with
my right hand at an arm's length is particularly convenient for
advancing through the images with my index finger unseen by the
camera. Also, my left hand is free for gesturing. However, this
is somewhat tiring for the right hand, and is unsuitable for
shoots longer than 5 minutes or so. For the longer shoots I hold
the camera with my left hand, and use the right hand to both
gesture and advance.
Staring into the camera is not a good idea, as this makes my eyes
look abnormally squinted. If I look at the visuals, then I indeed
appear to be looking at the visuals during the video. This is an
unplanned bonus. When I am not looking at the visuals, I prefer
to look away casually with only occasional glances at the
camera. I generally walk at a leisurely pace during the shoot.
Everytime I move to a new topic, I change my location. I shoot
the location for a few seconds both as an establishing shot, as
well as a restful prelude.
This stage produces two types of output: the recorded videos and
a cue.txt file in each topic folder inside the transfer folder.
If we need a screencast, then we use kazam. This produces a third
type of output: a screencast mp4 file.
This stage starts with transfering the output of the shooting
stage into the laptop. All the videos are stored inside the vid
folder, and the cue.txt files are to be copied to the appropriate
topic folders. This latter job is facilitate by the docue
<lecture number> command
(to be issued from inside (a copy of) the transfer folder of my
phone.
Now we need to run the command doamelt from inside the lecture
folder. This will create an mlt forlder under the lecture folder,
and populate with mlt files based on the cue.txt files in each
topic folder. If there is some animation, then I need to modify
the appropriate cue.txt file by adding the triple
"<png root> <start number> <end number" to the line after
where the animation is to be inserted.
Next it is time to launch kdenlive and load the template file.
We import the vid and the mlt folder. Typically, I shoot the
videos in order (including the estbalishing shots). So I can drag
and drop all the videos on the timeline in order. Then I add the
mlt files and sync them. After that I edit off the initial and
final parts of each. Then I add the separators and trim the
estbalishing shots. Finally, I add the separator slides and
render the movie to webm format. Then outside kdenlive I apply
dosanxep to compress the rendered video.
if [ ! $# == 1 ]; then
echo "Usage: domakelec <n>"
exit -1
fi
echo "Creating lec$1"
mkdir lec$1
cd lec$1
pwd
mkdir vid
cp $c/template.kdenlive lec$1.kdenlive
if [ $# != 3 ]; then
echo "Usage: doanim <svg root> <png root> <nframe>"
echo "There should be a file <svg root>1.svg in the working directory."
echo "It should have reference to <png root>0001.png in it."
echo "This program will create <svg root><i>.svg for i from 1 to <nframe>."
echo "<svg root><i>.svg is same as <svg root>1.svg except that"
echo "the references to <png root>0001.png will be changed to "
echo "<png root><j>.png, where j is i 0-padded up to 4 digits."
exit 1
fi
for((i=2;i<=$3;i++)); do
echo $i
printf -v j "%04g" $i
sed -r "s/${2}0001/$2$j/g" ${1}1.svg > $1$i.svg
done
echo $v
java -classpath ~/na/v/android AndroViewer $*
Here we are using the AndroViewer.java program.
folder="lec$1"
echo "Folder is [$folder]"
declare -a fldextlist
mapfile fldextlist < $c/$folder/order.txt
i=0
for fldext in ${fldextlist[*]} ; do
fld="${fldext%,*}"
i=$((i+1))
echo "$i --> $c/$folder/$fld/"
cp $i/cue.txt $c/$folder/$fld/
done
if [ ! -e order.txt ]; then
echo "No order.txt here!"
exit 1
fi
declare -a fldextlist
mapfile fldextlist < order.txt
if [ ! -e mlt ]; then
mkdir mlt
fi
rm mlt/*.mlt
for fldext in ${fldextlist[*]} ; do
fld="${fldext%,*}"
java -classpath ~/na/v/android Melter $fld
mv $fld.mlt mlt/
done
This script uses the Melter.java
program.
if [ ! -e order.txt ]; then
echo Error: No order.txt here.
exit 1
fi
declare -a fldextlist
mapfile fldextlist < order.txt
for fldext in ${fldextlist[*]} ; do
fld="${fldext%,*}"
ext="${fldext#*,}"
echo Folder=$fld , Ext=$ext
cd $fld
for f in *.svg; do
extension="${f##*.}"
filename="${f%.*}"
echo -n "$f -> $filename.png : "
if [ ! -e $filename.png ] | [ $f -nt $filename.png ]; then
echo "Converting."
inkscape -h 480 $f -e $filename.png
else
echo "Already done."
fi
done
if [ $# == 1 ]; then
echo Creating index.txt
ls -1 $ext[0-9].png > index.txt
more index.txt
fi
cd ..
done
ffmpeg -i $1.webm -vcodec libx265 -crf 28 -strict -2 $1.mp4
This script creates an animation (in the form of an image
sequence) building up some diagram. It consists of
the process
function, and some supporting routines.
md = function(x) if(!dir.exists(x)) dir.create(x)
scrn = function(xlo, xhi, ylo, yhi,...) {
bareplot(0,xlim=c(xlo,xhi),ylim=c(ylo,yhi),ty='n',...)
}
spStart = function(fname) {
png(fname,width=640)#,bg='transparent')
}
spEnd = function() dev.off()
process = function(xs,xe,ys,ye,rt,fun, nf, dur=1, ps=0, pe=0, ...) {
md(rt)
spStart(paste(rt,'/pic%04d.png',sep=''))
filename = sprintf('%s.lst',rt)
cat("FPS",nf,"\n",file=filename)
for(i in 1:nf) {
scrn(xs,xe,ys,ye,...)
pnow = ps + (i-1)/(nf-1)*(pe-ps)
fun(i,pnow) # i is the frame number (int, so exact),
# pnow is param value (may be approx)
cat(sprintf('%s/pic%04d.png\n',rt,i),file=filename,append=TRUE)
}
spEnd()
}
- The first four parameters set up the stage
area: $[xs,xe]\times[ys,ye].$
- The next
parameter,
rt
, is folder root. If it is "abc",
then a folder with that name is created (if it is not already
existing), and the generated image files are named
like abc/pic0001.png, abc/pic0002.png, etc. Also
a list file abc.lst
is created for loading into
SynfigStudio. The FPS is computed to make the durarion dur
sec.
- The next two
parameters,
ps
and pe
, denote the start
and end values of the parameter. Here ps
may be
greater than pe
.
- The
fun
parameter is a
function with a single argument, the current parameter value. It
is responsible for all the drawing. It must not start a new
plot (e.g., must not invoke plot
), but use functions
like lines
and points
to add to an
existing plot.
- Next comes the
nf
parameter, which
denotes the number of frames (i.e., the number of images in the
generated sequence). All additional parameters are sent to
the underlying plot routine.
The following code uses this script:
###-----------------------
genDat = function(mn,sd) {
x = rnorm(10,sd=sd)
x - mean(x) + mn
}
x1 = genDat(0,0.2)
x2 = genDat(1,0.2)
x3 = genDat(2,0.2)
xall = c(x1,x2,x3)
y1 = genDat(0,2)
y2 = genDat(1,2)
y3 = genDat(2,2)
yall = c(y1,y2,y3)
plot(c(x1,x2,x3),c(y1,y2,y3))
zr = rep(0,30)
clr = c(rep('red',10),rep('blue',10),rep('green',10))
f = function(i,t) {
abline(h=0,lwd=3)
points((1-t)*xall+t*yall,zr,col=clr,pch=20,cex=3)
}
process(min(yall),max(yall), 'test', 0,1,f,30,-0.2, 0.2 )