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://127.0.0.1:2200 into the "Server" field. After that, I can launch the same ffmpeg command, changing only the input parameter:

ffmpeg -i tcp://127.0.0.1:2200/?listen -vcodec mpeg2video -flags cgop+ilme -sc_threshold 1000000000 -filter:v fps=30 -b:v 16M -maxrate:v 31220000 -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


3 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.

  • R6WAX

    Hi, thx u so much. I’m working hard and already have TX on radioamateur 70cm bandplan between 430-432MHz fo “digital mode” and now have fully legal T2 1.7MHz DATV on classic T2 supports 1.7MHz BW )) What about maxrate value in your fdmpwg examles? Why it’s bigger than muxrate? I’m lost a lot of time for fighting with ffmpeg, finally, today i have sucess with converting to right muxrate. But still have problem with OBS Virtual camera and VB audio virtual cable. Need check you gstreamer example.

  • Ilya_MZP Author Reply

    R6WAX:
    Hi, thx u so much. I’m working hard and already have TX on radioamateur 70cm bandplan between 430-432MHz fo “digital mode” and now have fully legal T2 1.7MHz DATV on classic T2 supports 1.7MHz BW )) What about maxrate value in your fdmpwg examles? Why it’s bigger than muxrate? I’m lost a lot of time for fighting with ffmpeg, finally, today i have sucess with converting to right muxrate. But still have problem with OBS Virtual camera and VB audio virtual cable. Need check you gstreamer example.

    Codec maxrate being bigger than muxrate must have been a mistake from me trying different bandwidths and forgetting to update the encoder parameters. A few months ago I bought a dvb-t2 receiver, so I tried to use gstreamer. And I couldn't make it work well. While the latency was lower, muxrate limiter caused issues a lot of issues when streaming both audio and video. Either audio or video would block the whole pipeline or they would desynchronize.