I have a Hackrf One and have been wanting to try and transmit some digital tv signal. It seemed quite hard when I started, but after just a few hours of trial and error I managed to get some very exciting results! I can now stream my computer screen and audio to a TV wirelessly without needing to connect anything to a TV.

My old TV only supports DVB-T, so I started with the dvbt_tx_8k example file in gnuradio's github repo. I grabbed an example ts file from dtv-utils repo and launched the GRC example, changing only a few things: ts stream file path, frequency (to 474MHz) and output sink to osmocom sink. On the TV I launched an automatic digital channel scan, and it worked! It found my transmission and started playing it. With the absolute maximum setting of TX power, I got roughly a couple of meters of working distance with a crappy antenna. Hackrf's transmit amplifier is not particularly powerful.

After trying the example file, I wanted to try and encode my own. That quickly became my main problem that I wasted quite a bit of time on. You need a constant and very specific rate for the video to play properly. That's why there is dvbtrate program in the repo that lets you calculate the TS bit-rate. I changed all the code rates in the example from 2/3 to 7/8 and used the 31668449.197861 muxrate provided by the calculator program. Here is the ffmpeg command I used:

dvbtrate 8
ffmpeg -i <some_input> -vcodec mpeg2video -flags cgop+ilme -sc_threshold 1000000000 -filter:v "fps=30" -b:v 16M -maxrate:v 38810400 -bufsize:v 9781248 -acodec ac3 -ac 2 -b:a 448k -ar 48000 -muxrate 31668449.197861 -pix_fmt yuv420p -f mpegts output.ts

This worked surprisingly well and the quality seemed quite ok. You can increase the video bitrate even further, just make sure that the video bitrate combined with the audio bitrate doesn't go outside the muxrate limit.

To stream in real time, I changed a few things. I changed the GRC to No GUI and changed run options to Run to Completion, replaced the file source with a File Descriptor source with 0'th file descriptor (stdin) and generated the python code. This lets me pipe the video stream directly to the python program in the terminal. Here is the grc file. And here is how I launch it:

yt-dlp -f 'bestvideo[height<=1080]+bestaudio/best[height<=1080]' <youtube_url> -o - | ffmpeg -i - -vcodec mpeg2video -flags cgop+ilme -sc_threshold 1000000000 -filter:v "fps=30,scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:-1:-1:color=black" -b:v 16M -maxrate:v 38810400 -bufsize:v 9781248 -acodec ac3 -ac 2 -b:a 448k -ar 48000  -muxrate 31668449.197861 -pix_fmt yuv420p -f mpegts pipe:1 | dvbt_tx.py

The added video filter also insures the proper resolution and aspect ratio. You can also play with it to combat the overscan if you can't disable it on your TV.

Now, if you use X11, you can just stream your computer screen and audio with ffmpeg directly. But because I use Sway and ffmpeg still doesn't support xdg-desktop-portal, I have to use OBS to capture the screen and audio. In obs I set my stream output to custom and put tcp:// into the "Server" field. After that, I can launch the same ffmpeg command, changing only the input parameter:

ffmpeg -i tcp:// -vcodec mpeg2video -flags cgop+ilme -sc_threshold 1000000000 -filter:v fps=30 -b:v 16M -maxrate:v 38810400 -bufsize:v 9781248 -acodec ac3 -ac 2 -b:a 448k -ar 48000  -muxrate 31668449.197861 -pix_fmt yuv420p -f mpegts pipe:1 | dvbt_tx.py

Now you can just launch the stream in obs, and it should work.

The same works with DVB-T2 and mpeg4 encoding, but my TV doesn't support them. There are examples in gnu radio repo, like this one: vv016-256qam34.grc
You just need to calculate the muxrate with dvbt2rate calculator:

dvbt2rate <channel bandwidth> <fft size> <guard interval> <number of data symbols> <number of FEC blocks> <code rate> <modulation> <frame size> <extended carrier> <pilot pattern> <L1 modulation>

With the parameters from vv016-256qam34.grc

1 Comments latest

  • Ilya_MZP Author

    You can also use gstreamer for lower latency and pipewire screen capture. To specify muxrate, use mpegtsmux plugin with bitrate=<muxrate> parameter. It took me a long while to find that this parameter exists.