Created
October 19, 2023 01:07
-
-
Save btoconnor/381f9d48ed0bedeae955ae0012b36455 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/tvapp/tuner/antenna.py b/tvapp/tuner/antenna.py | |
index f6c9615..f34105b 100644 | |
--- a/tvapp/tuner/antenna.py | |
+++ b/tvapp/tuner/antenna.py | |
@@ -85,119 +85,136 @@ class AntennaTuner(Tuner): | |
"WMPB", # PBS | |
"CW", # CW | |
] | |
def __init__(self, url, prefix, type_, channels, channel_file_location): | |
"""Initialize Antenna tuner. | |
We just pass in the adapter number to pass to dvbv5-zap as url. | |
It's not great, but whatever. Instead of passing in a raw integer to | |
the base Tuner class, just pass the original string so that it's more | |
obvious in logs / debugging. | |
""" | |
Tuner.__init__(self, "unused_str_adapter_%s" % url, prefix, type_) | |
self.encoder_cmd = None | |
self.tuner_cmd = None | |
+ self.monitor_cmd = None | |
self.channel_proc = None | |
self.ffmpeg_proc = None | |
+ self.monitor_proc = None | |
self.adapter_number = url | |
self.channels = channels | |
log.debug("Channels: %s", self.channels) | |
self.channel_file_location = channel_file_location | |
def change_stream(self, req_channel, req_quality): | |
"""This is the entry point | |
For the direct antenna tuner, we need to construct one big command | |
that instructs dvbv5-zap to enter record mode and pipe the output | |
directly to the input of ffmpeg. | |
As far as I can tell, this means that much like the HDHR tuner, | |
when the channel or the quality is changed we'll need to restart | |
ffmpeg. | |
""" | |
tuner_cmd = self._build_channel_command(req_channel) | |
encoder_cmd = self._build_encoder_command(req_channel, req_quality) | |
+ monitor_cmd = self._build_monitor_command() | |
logging.info("Changing channel to {}".format(req_channel)) | |
# If either the tuner command or the encoder command has changed, we're | |
# going to restart the process. | |
if (tuner_cmd != self.tuner_cmd) or (encoder_cmd != self.encoder_cmd): | |
# Restart ffmpeg. | |
log.debug("Changing stream requires stopping!") | |
self.tuner_cmd = tuner_cmd | |
self.encoder_cmd = encoder_cmd | |
+ self.monitor_cmd = monitor_cmd | |
self.stop_stream() | |
def _build_tunerproc(self): | |
# We're piping the output of dvbv5-zap directly to ffmpeg, so we need | |
# to create our own tunerproc. This overrides what is happening | |
# in the base tuner. | |
default_channel = self._default_channel() | |
default_quality = 4 | |
if self.tuner_cmd is None: | |
self.tuner_cmd = self._build_channel_command(default_channel) | |
if self.encoder_cmd is None: | |
self.encoder_cmd = self._build_encoder_command(default_channel, default_quality) | |
+ if self.monitor_cmd is None: | |
+ self.monitor_cmd = self._build_monitor_command() | |
+ | |
self.channel_proc = subprocess.Popen(self.tuner_cmd, stdout=subprocess.PIPE) | |
self.ffmpeg_proc = subprocess.Popen( | |
self.encoder_cmd, | |
stdin=self.channel_proc.stdout, | |
stderr=subprocess.PIPE | |
) | |
+ self.monitor_proc = subprocess.Popen( | |
+ self.monitor_cmd, | |
+ stderr=subprocess.PIPE | |
+ ) | |
+ | |
return self.ffmpeg_proc | |
def _stop_tuner_proc(self): | |
"""Stop the processes associated with the stream. | |
Overriding because the antenna tuner has two procs and the | |
base tuner isn't handling that. There might be a better way than this | |
but I'm only looking to see if this works. | |
""" | |
if self.ffmpeg_proc.poll() is None: | |
self.ffmpeg_proc.send_signal(subprocess.signal.SIGTERM) | |
if self.channel_proc.poll() is None: | |
self.channel_proc.send_signal(subprocess.signal.SIGTERM) | |
+ if self.monitor_proc.poll() is None: | |
+ self.monitor_proc.send_signal(subprocess.signal.SIGTERM) | |
+ | |
st = time.time() | |
- for proc in [self.ffmpeg_proc, self.channel_proc]: | |
+ for proc in [self.ffmpeg_proc, self.channel_proc, self.monitor_proc]: | |
while True: | |
# Kill the process if it takes longer than 3 seconds | |
# to terminate gracefully | |
if proc.poll() is not None: | |
break | |
if time.time() - st > 3.0: | |
proc.send_signal(subprocess.signal.SIGKILL) | |
- self.stop_was_kill = True # Do we need to update this?? | |
+ self.stop_was_kill = True # Do we need to update this?? | |
break | |
self.ffmpeg_proc.wait() | |
self.channel_proc.wait() | |
+ self.monitor_proc.wait() | |
self.ffmpeg_proc = None | |
self.channel_proc = None | |
+ self.monitor_proc = None | |
# Need to set this as well because this is what the base tuner | |
# is watching to detect if needs to stop the tuner procs. | |
self.tunerproc = None | |
def validate_channel(self, channel): | |
# Make sure the requested channel is in the configuration file. | |
if channel not in self.channels: | |
log.info("Channel %s not in %s", channel, self.channels) | |
return self._default_channel() | |
return channel | |
def _build_channel_command(self, channel): | |
"Construct command for dvbv5-zap" | |
@@ -230,30 +247,38 @@ class AntennaTuner(Tuner): | |
"-i", "-", | |
] | |
if quality is None: | |
quality = self.quality | |
command += self._build_video_opts(channel, quality) | |
command += self._build_audio_opts(quality) | |
command += self._build_audio_channel_opts(quality) | |
command += self._build_subtitle_opts() | |
command += self.hlscmds | |
return command | |
+ def _build_monitor_command(self): | |
+ # dvb-fe-tool -a 0 -m | |
+ return [ | |
+ "dvb-fe-tool", | |
+ "-a", self.adapter_number, | |
+ "-m" | |
+ ] | |
+ | |
def _build_audio_channel_opts(self, quality): | |
"Build ffmpeg command opts for audio channels" | |
num_audio_channels = self.AUDIO_CHANNELS[quality] | |
return [ | |
"-ac", num_audio_channels | |
] | |
def _build_video_opts(self, channel, quality): | |
"Build ffmpeg command opts for video encoding" | |
# Values from http://www.lighterra.com/papers/videoencodingh264/ | |
opts = [] | |
video_bitrate = self.VIDEO_QUALITIES[quality] | |
video_size = self.VIDEO_RESOLUTIONS_P[quality] | |
diff --git a/tvapp/tuner/base.py b/tvapp/tuner/base.py | |
index fb10b38..9a12e96 100644 | |
--- a/tvapp/tuner/base.py | |
+++ b/tvapp/tuner/base.py | |
@@ -167,30 +167,35 @@ class Tuner: | |
# self.tunerproc.wait() | |
# self.tunerproc = None | |
subprocess.call("rm " + STREAMDIR+self.prefix+"*", shell=True) | |
self.status = 'Stopped' | |
def check_stream(self, curtime): | |
if self.tunerproc.poll() is not None: | |
# Stream died | |
self.stop_stream(abnormal=True) | |
time.sleep(1) | |
return | |
res = select.select([self.tunerproc.stderr], [], [], 0.1) | |
if res[0]: | |
self.updatetime = curtime | |
self.tunerproc.stderr.read() | |
+ | |
+ # TODO: Grab output from monitor proc for CNR and SNR values | |
+ # Sample message: | |
+ # `Lock (0x1f) Signal= -13.00dBm C/N= 23.20dB` | |
+ | |
return | |
if (curtime - self.updatetime > FREEZETIME | |
and curtime - self.start_time > FREEZESTARTTIME): | |
# Tuner got stuck - restart it | |
logging.info( | |
"Stopping stream because frozen: curtime: {}, updatetime: {}, start time: {}".format( | |
curtime, self.updatetime, self.start_time | |
) | |
) | |
self.stop_stream(abnormal=True) | |
# Tuner customization | |
def prepare_stream(self): | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment