Automating video generation by adding an intro and a trailing video to the main video

1

I have a video project I am trying to compile. Here is the overview:

  1. I have many videos which are 5 minute training sessions - Main video.
  2. The Intro Video will be a standard 5 second video that will have the Video title and Author. This will be concatenated to the main video.
  3. The Trailing Video will pretty much be a stock video that will be concatenated to the main video and have all the legaleze etc.
  4. The Intro Vid will smoothly fade into the main vid as well as when you get to end of the main video it will fade into the Trailing video nicely.
  5. The product is a new video with a Intro, Main & Trailer video all in one!

The concept is really that simple. In fact I found an example of a person who has solved this and is doing exactly what I want.

This solution is a Bash script that takes a config file that has the title, author, etc. and generates the Intro, the Ending and creates the resulting video with them concatenated.

I am using Ubuntu 12.04 Server. I have been trying to take this as a sample and just running it with no luck because of incompatibility errors. I even attempted to convert it using .MP4 containers or .MKV. I am running into error after error or incompatibility issues. I went as far as changing out the ffmpeg binary using the 25 Oct 2013 version from http://ffmpeg.gusari.org/static/64bit/ which I like as I don't have to worry about rebuilding the binary. Almost successful but again I have some error which I cannot solve.

I know part of the problem is the fact that video production, codecs, formats is a completely new field for me so I am attempting to work through this new territory.

Perhaps an expert here has something similar that I can use as a guideline that uses MP4 or h.264 format. Or take the solution above from the URL and make it work with a more up-to-date version of ffmpeg.

I will include the script and its parameter file and the output (abbreviated because of limitation) below. Basically as the script stands right now, when run I get the error [matroska,webm @ 0x27bbee0] Read error. This error is return from the 'reasembleVideo' routine from the first ffmpeg command.

The following is the Parameter File:

#!/bin/bash
INPUTFILE="ssh_main.mp4"
LOGO="logo.png"
LOGOLENGTH="1"
SPEAKER="Jason"
TITLE="Basic SSH Video"
DATE="October 28, 2013"
SCENESTART="00:00:01"
SCENEDURATION="00:00:09"
OUTPUTFILE="ssh_basic_1"
}

The following is the script I am running. The ${OUTPUTFILE} being used is a small 2 minute video I create in screen-o-matic in MP4 format.

Script on PasteBin (too long for Super User post)

DevDewboy

Posted 2013-11-01T23:15:19.787

Reputation: 41

1" I have some error which I cannot solve." Adding precisely which error might actually help. – Hennes – 2013-11-02T00:14:22.387

1Please write the full script you are using. Please give the full console output you are getting. What you want to achieve is understood. – Rajib – 2013-11-02T18:05:15.487

Is there an upload feature I can put the sample and script? Otherwise I'll put the info up tomorrow morning – DevDewboy – 2013-11-03T15:53:47.460

Add in the script and console output into your question space itself. To separate out the script (code) use backtics. See the markdown formatting by clicking on "learn More" that appears every time you click to add a comment. – Rajib – 2013-11-04T03:45:31.557

@Rajib I got the data you needed. Let me know if you need anything else. – DevDewboy – 2013-11-04T19:21:26.970

Answers

1

In a production environment when there are many videos to join, to automate the process you could break it into pretty much four components. Lets say you have 100 main training videos to begin with. These are named main001.mov, main002.mov and so on till main100.mov.

  • Create your 100 title cards as png or other format- through imagemagick/graphicsmagick/gimp/photoshop/whatever.
  • Convert these to mov files in ffmpeg. I show using a prepared stereo sound file which is silent, instead of bringing in from a null device, but that would be fine too. This creates a 5 second title with fade in and fade out:
#!/bin/bash  

for i in *.png;  
do  
ffmpeg -y -loop 1 -f image2 -i $i -i silence.wav -t 00:00:05 -s 1920x1080 -aspect 16:9 -acodec pcm_s24le -b:a 128k -vcodec qtrle -b:v 1024k -filter_complex 'fade=in:0:10, fade=out:110:10' -f mov ${i%%.png}.mov

done

This creates (100) mov titles from the supplied png files.

  • Create your end card as a png and convert to mov as before, only now you can say:
ffmpeg -y -loop 1 -f image2 -i endcard.png -i silence.wav -t 00:00:05 -s 1920x1080 -aspect 16:9 -acodec pcm_s24le -b:a 128k -vcodec qtrle -b:v 1024k -filter_complex 'fade=in:0:10, fade=out:110:10' -f mov endcard.mov
  • Concatenate three sets of files, the first 2 sets have 100 files each, title and main, and the third containing only one common end file. For this you put the file names of the sets in three arrays, and loop through the indices of the array to join them like so:
#!/bin/bash  

# define the movie array  

movie=("main001.mov" "main002.mov" .....and so on till ... "main100.mov")  

#define titles array  

title=("title001.mov" "title002.mov" .....and so on till ... "title100.mov")  

#define end card array  

endcard=("endcard.mov")  

# Use for loop  
# get total subscripts in an array  

total=${#movie[*]}  

for (( i=0; i<=$(( $total -1 )); i++ ))  
do  
ffmpeg -i ${title[i]} -i ${movie[i]} -i ${endcard[0]} -y -filter_complex '[0:0] setsar=1/1[sarfix];[sarfix] [1:0]  [2:0] concat=n=3:v=1:a=0 [v]' -map '[v]' -pass 1 -strict -2 -an -vcodec libx264 -pix_fmt yuv420p -aspect 16:9 -threads 4 -b:v 1024k -flags +loop -cmp chroma -partitions +parti4x4+partp8x8+partb8x8 -subq 1 -trellis 0 -refs 1 -bf 3 -b_strategy 2 -coder 1 -me_range 16 -g 250 -keyint_min 75 -sc_threshold 40 -i_qfactor 0.71 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 ${movie[i]%%.mov}.mp4

ffmpeg -i ${title[i]} -i ${movie[i]} -i ${endcard[0]} -y -filter_complex '[0:0] setsar=1/1[sarfix];[sarfix] [0:1] [1:0] [1:1] [2:0] [2:1] concat=n=3:v=1:a=1[v] [a]' -map '[v]' -map '[a]' -strict -2 -acodec aac -b:a 128k -pass 2 -vcodec libx264 -pix_fmt yuv420p -aspect 16:9 -threads 4 -b:v 1024k -flags +loop -cmp chroma -partitions +parti4x4+partp8x8+partb8x8 -mixed-refs 1 -subq 6 -trellis 1 -refs 5 -bf 3 -b_strategy 2 -coder 1 -me_range 16 -g 250 -keyint_min 75 -sc_threshold 40 -i_qfactor 0.71 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 ${movie[i]%%.mov}.mp4

done

Pass 1 does not take into account audio. The index of endcard array is static at 0, since there is only one. Of course, you can directly pass the name of the file here instead of using array. Sarfix MAY be required if the title movie or even the endcard movie causes problems. I have not shown the sarfix option for the end card movie- that would require setsar=1/1[sarfix];[sarfix] after the [2:0] mapping. Note also the sizes and bitrates may not apply for you.

In this way you can automate concatenating large number of main files with titles and end cards.

Rajib

Posted 2013-11-01T23:15:19.787

Reputation: 2 406

0

Do you have 2 ${OUTPUTFILE}.mkv? One for the main input and one for the final output? Change the final one explicitly to something like ${FINALOUTPUT}.mkv. You are using the concat protocol on mkv format. You should use the concat demuxer, or change your entire intermediate files to mpeg or .ts formats. Also:

Put the rate - r 29.97 after -i "${OUTPUTFILE}-frames"/frame%d.png.
So instead of:
./ffmpeg -threads 4 -r 29.97 -f image2 -i "${OUTPUTFILE}-frames"/frame%d.png \ -i "${OUTPUTFILE}.mkv" -c:a copy \ -map 0:0 -map 1:0 -f MP4 -y "${OUTPUTFILE}.mkv"

Use:
./ffmpeg -threads 4 -f image2 -i "${OUTPUTFILE}-frames"/frame%d.png -r 29.97 \ -i "${OUTPUTFILE}.mkv" -c:a copy \ -map 0:0 -map 1:0 -f MP4 -y "${OUTPUTFILE}.mkv"

In the concatenation phase, try without rate:
./ffmpeg -threads 4 \ -i concat:"${OUTPUTFILE}-intro.mkv"\|"${OUTPUTFILE}.mkv"\|"${OUTPUTFILE}-exit.mkv" \ -y "${FINALOUTPUT}.mkv"

If this does not work, you must use the other concat options such as the concate demuxer:
ffmpeg -f concat -i mylist.txt -c copy output
Where the mylist.txt contains a list of the three generated files.
See this.

There are also other issues with one stream defaulting to yuv444p and the other being explicitly yuv420p. This should have -pix_fmt yuv420p stated explicitly:
ffmpeg -loop 1 -i "${OUTPUTFILE}-intro.png" -q:v 1 -r 29.97 -t ${LOGOLENGTH} -y -f MP4 "${OUTPUTFILE}-logo1.mkv"
as also the next stage:
ffmpeg -i "${OUTPUTFILE}-logo1.mkv" -i "silence.mkv" \ -c:v copy -c:a copy -map 0:0 -map 1:0 -threads 4 \ -y -f MP4 "${OUTPUTFILE}-intro.mkv"

Rajib

Posted 2013-11-01T23:15:19.787

Reputation: 2 406

For your own sanity, don't use a single long script to do 3 or 4 tasks- separate out the scripts at least in the testing phase until they all work. It seems the frame rate is what is fouling up when ffmpeg gets to the last frame of the title video during concatenation. – Rajib – 2013-11-05T07:52:50.193

I just realized that all your inputs and outputs seem to have the variable ${OUTPUTFILE}. Can't you name the variables for each phase more specifically such as ${TITLEWITHSOUND} etc? – Rajib – 2013-11-05T08:41:43.063

This was my first answer but as there are many probable areas of worry, I just gave a fresh and cleaner way to concat several files at one go. That answer got pushed up to the top. However, I believe the script you are using does deserve to be looked into and cleaned up and rectified. – Rajib – 2013-11-05T12:37:24.503