added stream sanity checks (sound, singe color)
This commit is contained in:
@@ -3,6 +3,7 @@ import os
|
||||
import logging
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from io import StringIO
|
||||
from logging.handlers import MemoryHandler
|
||||
from pprint import pprint
|
||||
@@ -124,6 +125,23 @@ def get_recorder_adapter(recorder_info: Union[dict, Recorder]) -> RecorderAdapte
|
||||
return rec
|
||||
|
||||
|
||||
def check_stream_sanity(recorder: Recorder, recorder_adapter: RecorderAdapter):
|
||||
if recorder.archive_stream1 is None and recorder.archive_stream2 is None: # fall back to default names and rtsp
|
||||
archive_stream_1_url = "rtsp://{}/{}".format(recorder_adapter.address, Config.DEFAULT_ARCHIVE_STREAM_1_NAME)
|
||||
archive_stream_2_url = "rtsp://{}/{}".format(recorder_adapter.address, Config.DEFAULT_ARCHIVE_STREAM_2_NAME)
|
||||
else:
|
||||
archive_stream_1_url = recorder.archive_stream1
|
||||
archive_stream_2_url = recorder.archive_stream2
|
||||
|
||||
for i in range(0, Config.STREAM_SANITY_CHECK_RETRIES):
|
||||
do_checks = False
|
||||
if do_checks:
|
||||
return True
|
||||
else:
|
||||
time.sleep(Config.STREAM_SANITY_CHECK_INTERVAL_SEC)
|
||||
return False # stream sanity check failed!
|
||||
|
||||
|
||||
def check_capture_agent_state(recorder_agent: Union[Recorder, dict]):
|
||||
if recorder_agent.get('offline', False):
|
||||
logger.info("OK - Recorder {} is in offline / maintenance mode".format(recorder_agent.get('name')))
|
||||
@@ -142,6 +160,7 @@ def check_capture_agent_state(recorder_agent: Union[Recorder, dict]):
|
||||
logger.info("OK – recorder {} is recording :)".format(recorder_agent.get('name')))
|
||||
with agent_states_lock:
|
||||
agent_states[recorder_agent.get('name')] = 'OK - recorder is recording'
|
||||
|
||||
else:
|
||||
logger.info(rec.get_recording_status())
|
||||
logger.error("FATAL - recorder {} must be recording but is not!!!!".format(recorder_agent.get('name')))
|
||||
132
backend/tools/recorder_streams_sanity_checks.py
Normal file
132
backend/tools/recorder_streams_sanity_checks.py
Normal file
@@ -0,0 +1,132 @@
|
||||
import io
|
||||
import sys
|
||||
|
||||
import ffmpeg
|
||||
import os
|
||||
import tempfile
|
||||
from PIL import Image
|
||||
from pydub import AudioSegment
|
||||
from pydub.playback import play
|
||||
|
||||
|
||||
def old_test():
|
||||
file_name = tempfile.gettempdir() + os.path.sep + "test.jpg"
|
||||
print(file_name)
|
||||
if os.path.exists(file_name):
|
||||
os.remove(file_name)
|
||||
process = (
|
||||
ffmpeg
|
||||
.input('rtsp://172.22.246.207/extron1')
|
||||
# .input('rtsp://172.22.246.207/extron3')
|
||||
.output(file_name, vframes=1)
|
||||
# .output('-', format='h264')
|
||||
.run(capture_stdout=True)
|
||||
)
|
||||
image = Image.open(file_name)
|
||||
r, g, b = image.split()
|
||||
print(r.histogram())
|
||||
print(g.histogram())
|
||||
print(b.histogram())
|
||||
image.show()
|
||||
|
||||
|
||||
# old_test()
|
||||
|
||||
def is_single_color_image(image):
|
||||
single_color_image = True
|
||||
color = {}
|
||||
count = 0
|
||||
color_channels = image.split()
|
||||
for c in color_channels: # r, g, b
|
||||
|
||||
hist = c.histogram()
|
||||
num_of_non_zero_values = len([v for v in hist if v != 0])
|
||||
if num_of_non_zero_values > 1:
|
||||
single_color_image = False
|
||||
break
|
||||
else:
|
||||
color[count] = [i for i in enumerate(hist) if i[1] != 0][0][0]
|
||||
count += 1
|
||||
return single_color_image, color
|
||||
|
||||
|
||||
def check_frame_is_valid(stream_url, raise_errors=True):
|
||||
try:
|
||||
frame, _ = (
|
||||
ffmpeg
|
||||
.input(stream_url)
|
||||
.output('pipe:', vframes=1, format='image2', vcodec='mjpeg')
|
||||
.run(capture_stdout=True, capture_stderr=True)
|
||||
)
|
||||
image = Image.open(io.BytesIO(frame))
|
||||
single_color_image, color = is_single_color_image(image)
|
||||
if not single_color_image:
|
||||
image.show()
|
||||
return True, "all good :-)"
|
||||
else:
|
||||
if all(map(lambda x: x == 0, color.values())):
|
||||
return False, "Image is entirely black! (color: {} (RGB))".format(
|
||||
':'.join([str(x) for x in color.values()]))
|
||||
return False, "Image has only one single color! (color: {} (RGB))".format(
|
||||
':'.join([str(x) for x in color.values()]))
|
||||
except ffmpeg.Error as e:
|
||||
msg = "Could not connect to stream URL or other error!"
|
||||
try:
|
||||
msg += " ({})".format(e.stderr.decode('utf-8').strip().split("\n")[-1])
|
||||
except IndexError:
|
||||
pass
|
||||
if raise_errors:
|
||||
raise Exception(msg)
|
||||
else:
|
||||
return False, msg
|
||||
|
||||
|
||||
# print(check_frame_is_valid('rtsp://172.22.246.207/extron2'))
|
||||
|
||||
def check_if_audio_is_valid(stream_url, sample_length_sec=3, lower_alert_limit_dBFS=-40.0, raise_errors=True):
|
||||
file_name = tempfile.NamedTemporaryFile(suffix='.aac').name
|
||||
if os.path.exists(file_name):
|
||||
os.remove(file_name)
|
||||
try:
|
||||
frame, _ = (
|
||||
ffmpeg
|
||||
.input(stream_url, t=sample_length_sec)
|
||||
.output(file_name)
|
||||
.run(capture_stdout=True, capture_stderr=True)
|
||||
)
|
||||
|
||||
sound = AudioSegment.from_file(file_name, "aac")
|
||||
# print(sound.dBFS)
|
||||
play(sound)
|
||||
if sound.max_dBFS == -float('inf'):
|
||||
return False, "No active audio signal detected!"
|
||||
elif sound.max_dBFS < lower_alert_limit_dBFS:
|
||||
return False, "Very low volume (< {} dBFS) detected! ({})".format(lower_alert_limit_dBFS, sound.max_dBFS)
|
||||
|
||||
return True, "good audio signal detected! ({} max dBFS in {}s sample)".format(sound.max_dBFS, sample_length_sec)
|
||||
except ffmpeg.Error as e:
|
||||
msg = "Could not connect to stream URL or other error!"
|
||||
try:
|
||||
msg += " ({})".format(e.stderr.decode('utf-8').strip().split("\n")[-1])
|
||||
except IndexError:
|
||||
pass
|
||||
if raise_errors:
|
||||
raise Exception(msg)
|
||||
else:
|
||||
return False, msg
|
||||
|
||||
print(check_if_audio_is_valid('rtsp://172.22.246.207/extron1'))
|
||||
|
||||
|
||||
def check_if_audio_is_valid_stream(stream_url, raise_errors=True):
|
||||
audio, _ = (
|
||||
ffmpeg
|
||||
.input(stream_url, t=2)
|
||||
.output('pipe:', f="adts", acodec='copy')
|
||||
.run(capture_stdout=False, capture_stderr=False)
|
||||
)
|
||||
audio = io.BytesIO(audio)
|
||||
sound = AudioSegment.from_file(audio, "aac")
|
||||
play(sound)
|
||||
|
||||
# check_if_audio_is_valid('rtsp://172.22.246.207/extron1')
|
||||
@@ -1,15 +0,0 @@
|
||||
import ffmpeg
|
||||
|
||||
packet_size = 4096
|
||||
|
||||
process = (
|
||||
ffmpeg
|
||||
.input('rtsp://172.22.246.207/extron1')
|
||||
#.input('rtsp://172.22.246.207/extron3')
|
||||
.output('/tmp/test.jpg', vframes=1)
|
||||
#.output('-', format='h264')
|
||||
.run_async(pipe_stdout=True)
|
||||
)
|
||||
|
||||
while process.poll() is None:
|
||||
packet = process.stdout.read(packet_size)
|
||||
Reference in New Issue
Block a user