Using ffmpeg to generate a transport stream – more details and how to add text overlays

Generating a valid transport stream for DATV Express without one of the listed PVR type of capture/encoder cards is well within the capabilities of modern computers so why are we reliant upon hardware encoders?

I first became aware of this hardware encoder approach with the launch of Digilite a few years ago. At that time software encoding in real time was very challenging so using a hardware encoder was a sensible way to go. More recently the availability of suitable encoder cards has become limited as the PVR market has moved on to embrace H264 and other hardware codecs. Cards do come up on popular auction sites however they can be other than advertised (as I found) and are not guaranteed to be in full working order. Further there is limited value investing in a digital television transmission system if you’re going to feed it with an analogue signal in the first place!

A solution was sought and experimentation done with tools like VLC, ffmpeg and avconv. In the event I found that ffmpeg provided the best early results so have developed a solution to the point where I can reliably generate a valid transport stream for DVB-S mode using Charles G4GUO DATV Express Server.

VLC appears perfectly capable of generating a suitable transport stream however I found it very difficult to locate specific documentation for things like setting PIDs and meta data and also experienced stability problems with it silently crashing mid-stream – not an ideal situation in the middle of a QSO!

I also found that if you’re using ffmpeg to generate a transport stream for DATV Express you can very simply add text and simple graphics overlays to the video feed using filters. This is important for DATV enthusiasts as we are required to give our call sign at intervals and what could be more convenient than a text overlay on screen. Of course if we move to a purely digital format we cannot add the overlay using a conventional overlay processor as the analogue signal is never available. I’ve tagged on a section at the end of this discussion detailing how to get started doing this.

Anatomy of the ffmpeg command line:

Take my earlier post “DATV Express with vMix using ffmpeg to generate the Transport Stream” This included a batch file to run on the Windows machine which launches ffmpeg to do the video and audio compression and assemble the transport stream.

The last line of the batch file:

start "Video feed to DATV Express" /high c:\ffmpeg\bin\ffmpeg -f dshow -i video="vMix Video" -f dshow -i audio="vMix Audio" -f mpeg2video -pix_fmt yuv420p -r 25 -s 720x576 -aspect 4:3 -qmin 2 -qmax 35 -b:v %VIDRATE%k -minrate %VIDRATE%k -maxrate %VIDRATE%k -bufsize %BUFSIZE%k -acodec mp2 -ab 128k -ac 2 -f mpegts -mpegts_original_network_id 1 -mpegts_transport_stream_id 1 -mpegts_service_id 1 -mpegts_pmt_start_pid 4096 -streamid 0:289 -streamid 1:337 -metadata service_provider="MYCALL" -metadata service_name="My Station ID" -y udp://

…looks pretty horrendous doesn’t it. Let’s break it down a bit!

start "Video feed to DATV Express" /high c:\ffmpeg\bin\ffmpeg

We could simply run ffmpeg but using Start makes the window prettier and allows us to set the process priority.

Type start /? into a Windows command prompt and it will tell you “Starts a separate window to run a specified program or command.”

“Video feed to DATV Express” becomes the window title

/high specifies that the program should run at high priority – we need this to ensure ffmpeg keeps the data flowing while Windows runs vMix in the foreground and checks for updates etc. etc. etc. in the background!

The rest of the line is the ffmpeg command. In a nutshell it tells ffmpeg to receive two data streams – one video, one audio, how to encode each of those streams, and what wrapper to put them in – the Transport Stream.

-f dshow -i video="vMix Video"

Tells ffmpeg to receive a video feed in DirectShow format  from the DirectShow device called “vMix Video”

-f dshow -i audio="vMix Audio"

Tells ffmpeg to receive a second feed – audio this time – from the DirectShow input called “vMix Audio”

-f mpeg2video -pix_fmt yuv420p -r 25 -s 720x576 -aspect 4:3 -qmin 2 -qmax 35 -b:v %VIDRATE%k -minrate %VIDRATE%k -maxrate %VIDRATE%k -bufsize %BUFSIZE%k

This lot specifies the video encoder to use – mpeg2video and various controlling parameters

-pix_fmt yuv420p specifies the pixel format otherwise ffmpeg will use yuv422p which won’t work!
 -r 25 frame rate 25 fps
 -s 720×576 frame size 720 wide by 576 high
 -aspect 4:3 aspect ratio 4:3
 -qmin minimum q value to use – higher values make the picture more blocky
 -qmax maximum q value to use – lower values keep the picture quality up at the expense of possibly running out of bandwidth causing total picture breakdown
-b:v %VIDRATE%k the nominal bitrate for the video stream. %VIDRATE% is substituted in the script for a calculated value depending on symbol rate and forward error correction selected. The trailing k just specifies * 1000 as we calculate it in kbps
-minrate %VIDRATE%k the minimum bitrate – set the same as nominal to give constant bitrate
-maxrate %VIDRATE%k the maximum bitrate – set the same as nominal to give constant bitrate
-bufsize %BUFSIZE%k buffer size to use for the encoder. If this is too big ffmpeg uses variable bitrate regardless of the min/maxrate settings so I choose to use 70% of the bitrate value.

There seems to be little documentation out there on exactly how these parameters should be set but these seem to give reasonable results.

-acodec mp2 -ab 128k -ac 2

This specifies the audio codec to use – mp2, the audio bitrate 128kbps and two channels.

-f mpegts -mpegts_original_network_id 1 -mpegts_transport_stream_id 1 -mpegts_service_id 1 -mpegts_pmt_start_pid 4096 -streamid 0:289 -streamid 1:337 -metadata service_provider="MYCALL" -metadata service_name="My Station ID"

This is the wrapper – we could probably get away with just the -f mpegts but the remaining parameters give us complete control over the various IDs and metadata to be carried in the stream.

 -mpegts_original_network_id 1
-mpegts_transport_stream_id 1
-mpegts_service_id 1
 -mpegts_pmt_start_pid 4096
 -streamid 0:289
 -streamid 1:337
 -metadata service_provider=”MYCALL”
 -metadata service_name=”My Station ID”

You’ll want to change these to suit your station. The stream ID’s for the two streams are important. I have found that matching the video stream ID is necessary to ensure the receiver sees the channel, matching the audio stream ID reduces the chance of the receiver muting the audio if two stations change over a bit quickly on a repeater input.


Just tells ffmpeg that I want to answer yes to any questions it might want to ask me – this is handy in a batch file but not strictly necessary in an interactive situation.


This specifies where we want to send the transport stream – in this case across the network via UDP with a specific packet size – which by the way is 7 * 188 but could have been any multiple of 188 up to 255 * 188 from what Charles G4GUO tells me.

Documentation on these and many other parameters are available in the ffmpeg / avconv man pages – type “man ffmpeg” at a linux command prompt or search online for “ffmpeg man”.

Text overlays

It’s a condition of the amateur licence that you give your call sign at start, end and periodically during transmission. ATV operators usually have their call sign on display either somewhere adjacent to them in their operating position or as a text overlay added to the video signal.

In your digital ATV station you will probably have a digital video camera, feeding your computer or transmitter via firwire, SDI or HDMI. There is no analogue video available to which a text overlay can be added.

Without an analogue video signal this has to be achieved by other means. Using software like vMix is one solution. This provides a very easy way to composite several layers into one picture including text overlays, graphics and even virtual sets. It is however, frustrating to have to devote an entire input to produce a simple call sign overlay!

Ffmpeg allows each of the streams to be processed through one or more “Filters” before encoding and packaging them into the final transport stream. This is achieved through the use of the -vf option.

One or more filters with their parameters are passed to the filter processor in a single quoted string:

... -vf "filter1=param1=value1:param2='stringvalue2', filter2=param3=value3" ...

string values need to be enclosed in single quotes: ‘stringvalue’
each parameter is separated by colon characters: :

So how to add a call sign? Here’s the line that I use:

-vf "drawtext=fontfile='C\:\\Windows\\Fonts\\verdanab.ttf':text='MW0LLK':x=60:y=34:fontsize=20:fontcolor=0xffffff7f"

Notice that we have to specify the font file to use. This example is for use on a Windows system – on Linux it will be something like:

-vf "drawtext=fontfile='/usr/share/fonts/truetype/freefont/FreeSans.ttf':text='MW0LLK':x=60:y=34:fontsize=20:fontcolor=0xffffff7f"

The key difference being the font path and file name – you can choose whatever font you want although there may be limitations of which I am unaware.

The remaining examples will use the Windows format – mainly because that is the ffmpeg command that I have to hand as I write. All the other parameters used work unchanged on both systems.

The position and size parameters are all in pixels. These will need to be changed to suit the particular screen size and aspect ratio in use. I positioned the text in a little from top and left to allow for overscan on television receivers. It also looks better not crammed into the corner!

The fontcolor parameter will accept colour names e.g. green, white etc. or hexadecimal colour values as either six digit 0xrrggbb or eight digit 0xrrggbbaa where rrggbb specify the rgb values and aa specifies the alpha or transparency.

Values for r, g and b range from 00 to ff where 00 is black and ff is full on.

Alpha values range from 00 (totally transparent) to ff (opaque)

Drop Shadows

To make the text visible even against a similar coloured background I chose to add a drop shadow. Alternatives could include a solid background colour box, or placing the text twice at different sizes and colours with the smaller size on top of the larger so it presents a coloured outline to the small text.

Here’s the version with the drop shadow added:

-vf "drawtext=fontfile='C\:\\Windows\\Fonts\\verdanab.ttf':text='MW0LLK':x=60:y=34:fontsize=20:fontcolor=0xffffff7f:shadowcolor=0x003f007f:shadowx=2:shadowy=2"

For the drop shadow you need to specify the shadow colour and an x and y offset.

Finally here’s the actual filter specification that I currently use when working through our local ATV repeater:

-vf "drawtext=fontfile='C\:\\Windows\\Fonts\\verdanab.ttf':text='MW0LLK':x=60:y=34:fontsize=20:fontcolor=0xffffff7f:shadowcolor=0x003f007f:shadowx=2:shadowy=2, drawtext=fontfile='C\:\\Windows\\Fonts\\verdanab.ttf':text='70cm DVB-S':x=60:y=52:fontsize=12:fontcolor=0xffffff9f:shadowcolor=0x00007f9f:shadowx=2:shadowy=2, drawtext=fontfile='C\:\\Windows\\Fonts\\verdanab.ttf':text='via GB3TM':x=60:y=65:fontsize=12:fontcolor=0xffffff9f:shadowcolor=0x00007f9f:shadowx=2:shadowy=2"

This is added to the middle of the ffmpeg command after the inputs have been fully specified and before the encoder specifications thus:

start "Video feed to DATV Express" /high c:\ffmpeg\bin\ffmpeg -f dshow -i video="vMix Video" -f dshow -i audio="vMix Audio" -vf "drawtext=fontfile='C\:\\Windows\\Fonts\\verdanab.ttf':text='MW0LLK':x=60:y=34:fontsize=20:fontcolor=0xffffff7f:shadowcolor=0x003f007f:shadowx=2:shadowy=2, drawtext=fontfile='C\:\\Windows\\Fonts\\verdanab.ttf':text='70cm DVB-S':x=60:y=52:fontsize=12:fontcolor=0xffffff9f:shadowcolor=0x00007f9f:shadowx=2:shadowy=2, drawtext=fontfile='C\:\\Windows\\Fonts\\verdanab.ttf':text='via GB3TM':x=60:y=65:fontsize=12:fontcolor=0xffffff9f:shadowcolor=0x00007f9f:shadowx=2:shadowy=2" -f mpeg2video -pix_fmt yuv420p -r 25 -s 720x576  -aspect 4:3 -qmin 2 -qmax 35 -b:v %VIDRATE%k -minrate %VIDRATE%k -maxrate %VIDRATE%k -bufsize %BUFSIZE%k -acodec mp2 -ab 128k -ac 2 -f mpegts -mpegts_original_network_id 1 -mpegts_transport_stream_id 1 -mpegts_service_id 1 -mpegts_pmt_start_pid 4096 -streamid 0:289 -streamid 1:337 -metadata service_provider="MW0LLK" -metadata service_name="MW0LLK-Express" -y udp://

You can replace the command in the batch file with this one – suitably modified to suit your call sign etc. stream IDs, meta data and output specification. Alternatively grab everything from -vf to the closing double quote at the end of the filters and paste it into the command in your batch file.

I have to add a disclaimer – all of the above is based upon much reading of the limited documentation available for ffmpeg / avconv and experimentation. I don’t have access to a proper stream analyser or Tutione software so there may well be errors in the stream that I am unaware of! The streams generated with the above commands do work with my satellite receiver and the one on GB3TM so they must be reasonably ok.

I hope this proves useful to somebody. I’ve broken down the ffmpeg command used to generate the TS for DATV Express and detailed how to add text overlays. Next time…. well I’ll have to think what to cover next. I am currently playing with Raspberry Pi V2 and DATV Express. It would be nice to box up the DATV Express with either a Raspberry Pi or Odroid as a stand-alone transmitter with web interface for settings and control.

DATV Express with vMix using ffmpeg to generate the Transport Stream

Out of the box DATV Express runs only with very specific hardware – typically Hauppauge capture cards with hardware MPEG2 encoders on board. Since I didn’t have a suitable card available I researched various ways to use the transmitter – the hard part being to create a suitable transport stream.

The choice of vMix was made because I’ve been using this software for a few months now on FM ATV and have found it to be a really useful way to generate a very slick feed from one or more cameras with useful additional features like text and graphics overlays, transitions and blends, desktop capture, green screen / chroma key and virtual sets – all done in real time. The free version of vMix is limited to just four inputs but that is sufficient for my purposes at present.

vMix does however require a half decent PC running Windows 7 or later, and DATV Express requires a Linux box running Ubuntu.

Here’s one of the recipe’s that I have found works reasonably well:


Note: You can use any suitable machines – these work for me!

The two computers need to be connected using a wired network – preferably not too busy. If you’re using wireless currently you can probably just hook the two machines together and set their wired network ports to fixed IP addresses on the same subnet e.g for the Windows box and for Linux.

Leave them connected to the wireless network for internet access – the wired connection will then only be used for DATV Express.

Make sure you’re using a different subnet to that used by your wireless network or there will be problems! Make a note of the IP address for your linux box as we’ll need that later. Check the machines can ping each other:
On the Windows box:


On the Linux box:


Both of these should result in replies being reported. Firewalls may complain – allow access!

Main laptop / vMix:
Install and configure vMix if you haven’t already got it. There’s some great video introductions on YouTube which will help with this.

Note – if you don’t want to use vMix you can use any other directShow camera or video capture device and audio input device – you’ll need to change the ffmpeg command below.
Provide some input e.g. from a camera or a colour – choose colour bars to get the vMix test card.
Set the external output (Settings / External Output) to PAL 25p 640×480 (or 720×576 different resolutions may or may not work with your receiver).
Enable the external output (Click External at bottom centre of the window)

Install ffmpeg from – I’m using the 32 bit static build.

Create a batch file e.g. vmix2tx.bat – put the following in it:

@echo off
rem vMix to Transport Stream for DATV Express
rem by Chris mw0llk January 2015.
rem with thanks to Rob m0dts for the linux shell script on which this was based.
echo Enter the following values to start the Tx, Press Enter for default (previous) values

if X%SR%==X set SR=2000
set defSR=%SR%
set /p SR=Enter SR in KS (%SR%):
if X%SR%==X set SR=%defSR%

if X%FEC%==X set FEC=5/6
set defFEC=%FEC%
set /p FEC=Enter FEC in KS (%FEC%):
if X%FEC%==X set FEC=%defFEC%

rem Save values for next time
rem If these lines cause errors your system may not support SETX
rem If so just comment them out
if not %SR%==%defSR% setx SR "%SR%"
if not %FEC%==%defFEC% setx FEC "%FEC%"

echo Symbol rate: %SR%, FEC: %FEC%. Calculating bitrates... 

rem Calculate Video bit rate value for current SR/FEC 
set /a DSR=2*%SR% 
echo DSR is %DSR% 

set /a DECFEC=1000*%FEC% 
set /a RS=188000/204 
set /a TS=%DSR% * %RS% / 1000 * %DECFEC% / 1000 
set /a TSMAUD=%TS%-128 
set /a VIDRATE=%TSMAUD% * 75 / 100 
set /a BUFSIZE=%VIDRATE% * 7 / 10 

echo Transport Stream rate for current SR/FEC is: %TS% 
echo Video bit-rate: %VIDRATE% 

start "Video feed to DATV Express" /high c:\ffmpeg\bin\ffmpeg -f dshow -i video="vMix Video" -f dshow -i audio="vMix Audio" -f mpeg2video -pix_fmt yuv420p -r 25 -s 640x480 -aspect 4:3 -qmin 2 -qmax 35 -b:v %VIDRATE%k -minrate %VIDRATE%k -maxrate %VIDRATE%k -bufsize %BUFSIZE%k -acodec mp2 -ab 128k -ac 2 -f mpegts -mpegts_original_network_id 1 -mpegts_transport_stream_id 1 -mpegts_service_id 1 -mpegts_pmt_start_pid 4096 -streamid 0:289 -streamid 1:337 -metadata service_provider="MYCALL" -metadata service_name="My Station ID" -y udp://

The start command is used to set the priority for ffmpeg to high – this helps ensure it gets sufficient cpu attention on my i3 – you may not need to do this – experiment once things are working using task manager – right click on ffmpeg in the processes tab and choose Set Priority.

You may need to change the path to ffmpeg depending where you installed it.

You will need to change the metadata tags to your own call sign and station ID, and change the IP address and port to suit your chosen linux box – see next step:

You may need to change one or more of the transport stream IDs depending upon your receiver settings and abilities. If you’ve not already got the receiver working with DATV Express leave the values as they are – when you tune your receiver it will pick up these values. If you’re sending to a repeater you may well need to change some of the IDs.

That’s it for now on the Windows box. Next we have to set up the Linux box to deliver the transport stream to the DATV Express card.


My netbook happens to run Lubuntu 14.04 – it could probably have been any Ubuntu variety though I have noticed that different distros either still have ffmpeg or have migrated to avconv. Either way both commands accept the same options so can be used interchangeably. We’re not going to use ffmpeg/avconv unless extra buffering is needed in this particular recipe.

All my tests with the DATV Express software from the main web site have failed. I’m not sure what the problem is but the only way I’ve found to get it working for now is to use the DATV Express Server software which Charles G4GUO has published via github. This does mean a little more work but it is straightforward and should work on most systems.

To date I’ve built it on an old Pentium 4 running Ubuntu 14.04, my netbook running Lubuntu 14.04 and my i3 laptop running Ubuntu Studio 12.10. I’ve also built it on two raspberry pi’s – a model b /512 and a new V2 pi. All have worked insofar as the software compiled successfully and ran.

In order to use the Express Server you’ll need to build it using Qt Creator (But see note added at the end of this). Use Lubuntu Software Centre, Synaptic Package Manager or apt-get to install Qt Creator, git and libusb-dev which is also needed.

sudo apt-get install qtcreator git libusb-dev

If you’ve never built any software or you’re using a newly set up system you’ll probably need to add gcc and qt4-dev to this list:

sudo apt-get install gcc qt4-dev qtcreator git libusb-dev

Expect it to take a while – make tea etc. as many packages will also be downloaded and installed to satisfy dependencies.
Create a suitable directory under your home

mkdir datv
cd datv

Grab the latest git repository for Express Server

git clone

This will create another sub-folder: express_server, and will populate it with the source code. Fire up Qt Creator and open ~/datv/express_server/

You’ll get a warning about No .user settings… – choose Yes

Accept the defaults in the Configure Project dialog… – choose Configure Project

Assuming no errors occurred hit Ctrl-B or choose

Build/Build Project "express_server"

Again assuming no errors occurred you should now have a file ~/datv/build-express_server-Desktop-Debug/express_server. You could run this but it would not be able to see the various firmware and configuration files included in the git repository. You’ll need to fire up a terminal window and navigate to the original git folder:

cd ~/datv/express_server

In that same folder you’ll find datvexpress.txt – edit this file to the desired values for frequency, FEC etc. Ensure that you set the tsock value to match the port number in the ffmpeg command line on the Windows machine (1234).

Connect DATV Express, power it up and start the server:


The server should start up and initialise the card by sending the various firmwares to it. After a pause you should see “UDP sockets and process threads are running”.

If your receiver is tuned and on you’ll see a blank screen rather than no signal – this shows all is well!

If it simply says it cannot find the DATV Express hardware you need to read the manual from the DATV Express web site – you’ll need to set the permissions by modifying a udev file.

Alternatively just run it as root:

sudo su

Sending the transport stream:

Go back to the Windows machine and launch the batch file we created earlier. Choose symbol rate and FEC to match the settings in datvexpress.txt on the linux box.

ffmpeg will launch in a new window and display something like:

Press [q] to stop, [?] for help
frame=  167 fps= 25 q=3.0 size=    1288kB time=00:00:06.60 bitrate=1599.0kbits/s dup=1 drop=0

The numbers will keep changing! If instead you get lots of error messages or buffer overflow warnings check and re-check the various IP addresses etc. and that the machines can communicate to each other (ping works). Also look for over zealous firewalls and other communication problems. There are diagnostic tools which can help – I’ll hopefully write up some of these in the near future.

If everything works whatever vMix is putting out to it’s external output should now be transmitted in DVB-S mode by DATV Express. If you’ve not already tuned in now it the time to figure out your receiver. I use a 5cm length of wire at transmitter and receiver for benchtop testing at 23cm frequencies. These can be directly received by most free to air satellite receivers.

You’ll need to set up a transponder on the corresponding frequency and symbol rate then scan for channels. On my receiver the transponder is set to 11030 MHz which when used with a 9750 LNB gives an IF of  1280mHz. Polarisation is irrelevant.

Controlling DATV Express

You can switch DATV Express ptt on and off by writing to a temporary file. Details on how to do this are in commands.txt in the source code directory on the Linux box.

I usually run a second terminal window or, if using Linux without XWindows running I switch between consoles (ctrl-alt-F1 ctrl-alt-F2 etc.) in order to issue commands.

Useful commands include:

echo "set freq 437000000" >> /tmp/expc
echo "set ptt rx" >> /tmp/expc
echo "set ptt tx" >> /tmp/expc
echo "set kill" >> /tmp/expc

…many more are documented. You can change things like FEC on the fly. If you do so you will need to stop and restart ffmpeg. In the ffmpeg window hit ‘q’ and it will safely terminate. Launch it again and set the new values for FEC or symbol rate.

Note that if you cheated and are running express server as root you’ll need to do

sudo su

before sending any of these commands. Doing sudo echo won’t work because of the redirected output!

The last command (set kill) is important – this gracefully shuts down the software and hardware – if you terminate it using ctrl-c or similar the board will continue trying to transmit which is undesirable!

Have fun…


Since I wrote this Charles G4GUO has kindly pointed out that the latest revision of Express Server on Github can now be built using make. This means that you do not need to install Qt Creator and qt4-dev but may need to install ‘make’:

sudo apt-get install gcc make git libusb-dev
sudo make
sudo make install

Note that I haven’t tested this!

Thanks for your input Charles!