This topic will give the instructions to install MythTV Master (v34) for running on a 4GB Raspberry Pi 4 (RPI4) running the latest Bookworm 64 bit based on Debian 12 either as a Frontend only or a combination Frontend/Backend (FE/BE). The MythTV developer team and the Raspberry Pi OS team both have made huge improvments to video routines and drivers. So this doc will only concentrate on master and Bookworm 64 bit which means you must have a v34 (Master) backend or do the combination build so you will have one on the RPI4. The RPI4 has no issues being a backend. You can easily record 4 TV HD programs from the networked tuners at once while playing back a previously recorded program on the frontend section of the RPI4.
I created this document while installing a combo FE/BE so the instructions should be as accurate as possible.
The first part is common to both, so we will start there. We are going to build from source so the latest will always be available for the version we want to work with.
We will be installing 'mythtv-light' so the software is packaged as a .deb file that can be easily installed and upgraded.
You can find the version of v34 as of today(12-2-2024) . That version is at: https://drive.google.com/drive/folders/ ... bVG77UdN2X
Pick the Bookworm version,
I'll try to keep the v34 Bookworm folders up to date.
The hardware being used in this example is:
RPI4 4GB RAM
USB3 to SATA Adapter based on the ASMedia ASM1153E controller
1TB SATA SSD
USB keyboard
FHD HDMI monitor
SiliconDust HDHomeRun Quatro networked ATSC 1.0 tuner (4 tuners) (HDHR)
Ethernet connected to the same network as HDHR with Cat-5e, but WiFi can work if yours is a very good connection.
Using the Raspberry Pi Imager 1.7.5 or newer, select the Bookworm 64bit. Flash the image directly to the SATA SSD using the adapter.
The version of Bookworm that I installed had as default a locale that uses the ISO settings. Previous versions uses UTF-8. You may need to change this.
You need to download or build mythtv-light and install similar to the command below:
Code: Select all
sudo apt install ./mythtv-light_34.0-30-gc376437559-0_arm64_bookworm.deb
See the build v34 topic to see a work around to avoid the warnings if you want.
At this point the MythTV software is installed and if you only want a frontend then you are done.
To build a combo FE/BE on v34 you will use a web app, but first some setup is needed.
Install mariadb-server
Code: Select all
sudo apt install mariadb-server
Code: Select all
sudo useradd -m mythtv
Code: Select all
sudo usermod -aG mythtv jim
Code: Select all
mkdir ~/build
cd ~/build
git clone https://github.com/mythtv/packaging
sudo cp -n ~/build/packaging/deb/debian/mythtv-backend.service /lib/systemd/system/
sudo systemctl enable mythtv-backend
sudo systemctl daemon-reload
Code: Select all
cd
nano create-dirs.sh
Code: Select all
#! /bin/bash
# Globals
mythtv_storagegroup_path=/srv/mythtv/
mythtv_storagegroups="banners coverart fanart recordings streaming videos bare-client db_backups livetv sports screenshots trailers music musicart"
sudo mkdir -p $mythtv_storagegroup_path
cd $mythtv_storagegroup_path
sudo mkdir -p $mythtv_storagegroups
sudo chown -R mythtv:mythtv $mythtv_storagegroup_path
sudo chmod -R 2775 $mythtv_storagegroup_path
Code: Select all
sudo ./create-dirs.sh
Now using your browser to to localhost:6544
Follow the instruction for setting up the database and select the mariadb button. Copy the code and built the sql script using the console.
Once the instructions on the database are complete, click save and go back to the top and click on Restart backend and make sure all the indicators are good and then click next.
On the General Backend Setup select the Primary IP to match your actual IP. Save
On the Job Queue (Backend-Specific) Turn off Commerical Flagging and Transcoding Jobs. Save
Next setup the Capture cards. Since my current HDHR is a 2 tuner version, I setup 2 tuners and since this test run is for over the air EIT EPG I select those options.
On Video Source I setup only a EIT source and name it OTA. You can use Schedules Direct EPG and the setup here would be about the same as standard SD setup.
On Input Connections I select the Name to MPEG2TS, Video source to OTA, and Scan Channels -> scan type to HDHR channel import. Then Start Scan. Next I select Starting Channel. Save
For the other tuner do the same except don't scan for this tuner.
Setup the Storage Directories. Setup the ones you want but the Default one is required. Since we setup the directories above in /srv/mythtv/, select the appropriate directory. The Default is usually 'recordings'.
When you are through using the Web App to setup the backend, click on the Restart Backend button and make sure it starts. You can go to the dashboard to view everything.
However, for mythtv-backend to start up properly after booting the RPI4, you are going to have to delay the startup of mythtv-backend until the HDHomerun tuners are discoverable.
Back to the console,
Code: Select all
sudo apt install hdhomerun-config
Code: Select all
sudo --login systemctl edit mythtv-backend.service
Code: Select all
[Service]
ExecStartPre=-/usr/local/bin/hdhomerun_check.py
can watch recordings etc.) If you want to prevent the backend from starting, remove the dash.
You'll need to put the hdhomerun_check.py in /usr/local/bin and make it executable, owner/group root.
hdhomerun_check.py
Code: Select all
#!/usr/bin/python3
# -*- coding: utf-8 -*-
""" See if the HD Homerun box(s) are accessible and running
Requires Python 3.6 or later.
For backends started by systemd, use:
sudo --preserve-env systemctl edit --force mythtv-backend.service
and enter or add as needed by your service:
[Service]
ExecStartPre=-/usr/local/bin/hdhomerun_check.py
Can be called with optional IP address(s) for users that
have multiple HDHRs that have STATIC addresses.
Use --help to see all options.
If run from the command line, then output will be to the screen.
Otherwise, a log file in /tmp named hdhr_discovery.log is made.
Changable with the --logfile switch.
Exit codes:
0 = success (for *ALL* HDHRs if multiple IPs were specified)
1 = no output from the hdhomerun_config discover command
2 = IPv4 and IPv6 addresses found, disable IPv6 on NIC
3 = logfile isn't writable, delete it and try again
4 = keyboard interrupt
5 x the number of HDHRs = HDHR is most likely not up
"""
__version__ = '1.28'
import argparse
import signal
import subprocess
import sys
from datetime import datetime
from os.path import basename
from os import _exit
from time import sleep
# pylint: disable=too-many-arguments,unused-argument
def keyboard_interrupt_handler(sigint, frame):
''' Handle all KeyboardInterrupts here. And, just leave. '''
_exit(4)
# pylint: enable=unused-argument
def get_program_arguments():
''' Process the command line. '''
parser = argparse.ArgumentParser(description='HDHR Access Test',
epilog='* Default values are in ()s')
parser.add_argument('HOSTS', type=str, default=None, nargs='*',
help='optional hostname(s)/IP(s) (%(default)s)')
parser.add_argument('--attempts', default=20, type=int, metavar='<num>',
help='number of tries to find HDHRs (%(default)i)')
parser.add_argument('--debug', action='store_true',
help='output additional information (%(default)s)')
parser.add_argument('--logfile', default='/tmp/hdhomerun_check.log',
type=str, metavar='<lf>',
help='optional path + name of log file (%(default)s)')
parser.add_argument('--sleep', default=1.5, type=float, metavar='<sec>',
help='seconds betweem attempts (%(default)s)')
parser.add_argument('--version', action='version',
version='%(prog)s ' + __version__)
return parser.parse_args()
def get_elapsed_time(start):
''' Calculate the time spent waiting for the HDHR to come up. '''
delta = datetime.utcnow() - start
rounded_delta = f'{delta.seconds + (delta.microseconds / 1000000):.3f}'
return rounded_delta
def log_or_print(loglevel, message, output):
''' Add timestamp, log level then print to the selected location. '''
print(datetime.now().strftime("%F %T.%f")[:-3], f'{loglevel:8}', message,
file=output)
def last_message(loglevel, result, host, start, attempt, output):
''' Common success or failure message text. '''
log_or_print(loglevel, f'{result} {"at " + host + " " if host else ""}'
f'in {get_elapsed_time(start)} seconds '
f'and {attempt} attempt{"s"[attempt == 1:]}\n', output)
def check_one_device(host, args, output):
''' Try to discover the HDHR(s). '''
attempt = 0
command = ['hdhomerun_config', 'discover']
start = datetime.utcnow()
if host:
command.append(host)
for attempt in range(1, args.attempts+1):
try:
discovery_response = subprocess.check_output(
command, text=True, stderr=subprocess.STDOUT).split()
except subprocess.CalledProcessError:
log_or_print('WARNING', f'{command[0]}: got no response, attempt: '
f'{attempt:2}', output)
sleep(args.sleep)
continue
if not discovery_response:
log_or_print('ERROR', f'No output from {command[0]}, aborting!',
output)
sys.exit(1)
if args.debug:
log_or_print('DEBUG', f'Got: {" ".join(discovery_response)}',
output)
if discovery_response.count('hdhomerun') > 1:
log_or_print('ERROR', f'{command[0]}: more than 1 IP, aborting!',
output)
sys.exit(2)
if discovery_response[0] != 'hdhomerun':
# Consider making this an ERROR and exiting not sleeping...
log_or_print('WARNING', f'{command[0]} got an unexpected response:'
f' {" ".join(discovery_response)}',
output)
sleep(args.sleep)
else:
last_message('INFO', f'Found HDHR {discovery_response[2]}', host,
start, attempt, output)
return 0
last_message('ERROR', 'No HDHR found', host, start, attempt, output)
return 5
def main(args, output=None):
''' Control checking of one or more devices. '''
log_or_print('INFO', f'Starting {basename(__file__)} v{__version__}, '
f'attempts={args.attempts}, sleep={args.sleep:.2f}', output)
if args.HOSTS:
return_value = 0
for host in args.HOSTS:
return_value += check_one_device(host, args, output)
else:
return_value = check_one_device(None, args, output)
return return_value
if __name__ == '__main__':
signal.signal(signal.SIGINT, keyboard_interrupt_handler)
ARGS = get_program_arguments()
if sys.stdin and sys.stdin.isatty():
RETURN_VALUE = main(ARGS)
else:
try:
with open(ARGS.logfile, encoding='ascii', mode='a') as file_obj:
RETURN_VALUE = main(ARGS, output=file_obj)
except PermissionError:
print(f'Can\'t write to {ARGS.logfile}, aborting!')
sys.exit(3)
sys.exit(RETURN_VALUE)
# vim: set expandtab tabstop=4 shiftwidth=4 smartindent colorcolumn=80:
If you are going to be running mythfrontend remotely from this backend you need to configure mariadb.
Create a file: /etc/mysql/mariadb.conf.d/80-mythtv,cnf
It should look like this:
Code: Select all
[mysqld]
bind-address=*
max_connections=100
binlog-ignore-db = mythconverg