Add listening audio probe diagnostics

This commit is contained in:
Smittix
2026-02-04 11:02:00 +00:00
parent 3f6b9cc5ef
commit 4bd7077d64

View File

@@ -346,7 +346,7 @@ def scanner_loop():
logger.info("Scanner thread stopped")
def _start_audio_stream(frequency: float, modulation: str):
def _start_audio_stream(frequency: float, modulation: str):
"""Start audio streaming at given frequency."""
global audio_process, audio_rtl_process, audio_running, audio_frequency, audio_modulation
@@ -442,43 +442,51 @@ def _start_audio_stream(frequency: float, modulation: str):
'pipe:1'
]
try:
# Use shell pipe for reliable streaming
# Log stderr to temp files for error diagnosis
rtl_stderr_log = '/tmp/rtl_fm_stderr.log'
ffmpeg_stderr_log = '/tmp/ffmpeg_stderr.log'
shell_cmd = f"{' '.join(sdr_cmd)} 2>{rtl_stderr_log} | {' '.join(encoder_cmd)} 2>{ffmpeg_stderr_log}"
logger.info(f"Starting audio: {frequency} MHz, mod={modulation}, device={scanner_config['device']}")
audio_rtl_process = None # Not used in shell mode
audio_process = subprocess.Popen(
shell_cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=0,
start_new_session=True # Create new process group for clean shutdown
)
# Brief delay to check if process started successfully
time.sleep(0.3)
if audio_process.poll() is not None:
# Read stderr from temp files
rtl_stderr = ''
ffmpeg_stderr = ''
try:
with open(rtl_stderr_log, 'r') as f:
rtl_stderr = f.read().strip()
except:
pass
try:
with open(ffmpeg_stderr_log, 'r') as f:
ffmpeg_stderr = f.read().strip()
except:
pass
logger.error(f"Audio pipeline exited immediately. rtl_fm stderr: {rtl_stderr}, ffmpeg stderr: {ffmpeg_stderr}")
return
try:
# Use shell pipe for reliable streaming
# Log stderr to temp files for error diagnosis
rtl_stderr_log = '/tmp/rtl_fm_stderr.log'
ffmpeg_stderr_log = '/tmp/ffmpeg_stderr.log'
shell_cmd = f"{' '.join(sdr_cmd)} 2>{rtl_stderr_log} | {' '.join(encoder_cmd)} 2>{ffmpeg_stderr_log}"
logger.info(f"Starting audio: {frequency} MHz, mod={modulation}, device={scanner_config['device']}")
audio_rtl_process = None # Not used in shell mode
audio_process = subprocess.Popen(
shell_cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=0,
start_new_session=True # Create new process group for clean shutdown
)
# Brief delay to check if process started successfully
time.sleep(0.3)
if audio_process.poll() is not None:
# Read stderr from temp files
rtl_stderr = ''
ffmpeg_stderr = ''
try:
with open(rtl_stderr_log, 'r') as f:
rtl_stderr = f.read().strip()
except:
pass
try:
with open(ffmpeg_stderr_log, 'r') as f:
ffmpeg_stderr = f.read().strip()
except:
pass
logger.error(f"Audio pipeline exited immediately. rtl_fm stderr: {rtl_stderr}, ffmpeg stderr: {ffmpeg_stderr}")
return
# Validate that audio is producing data quickly
try:
ready, _, _ = select.select([audio_process.stdout], [], [], 2.0)
if not ready:
logger.warning("Audio pipeline produced no data in startup window")
except Exception as e:
logger.warning(f"Audio startup check failed: {e}")
audio_running = True
audio_frequency = frequency
@@ -878,6 +886,7 @@ def audio_debug() -> Response:
"""Get audio debug status and recent stderr logs."""
rtl_log_path = '/tmp/rtl_fm_stderr.log'
ffmpeg_log_path = '/tmp/ffmpeg_stderr.log'
sample_path = '/tmp/audio_probe.bin'
def _read_log(path: str) -> str:
try:
@@ -897,7 +906,34 @@ def audio_debug() -> Response:
'audio_process_alive': bool(audio_process and audio_process.poll() is None),
'rtl_fm_stderr': _read_log(rtl_log_path),
'ffmpeg_stderr': _read_log(ffmpeg_log_path),
'audio_probe_bytes': os.path.getsize(sample_path) if os.path.exists(sample_path) else 0,
})
@listening_post_bp.route('/audio/probe')
def audio_probe() -> Response:
"""Grab a small chunk of audio bytes from the pipeline for debugging."""
global audio_process
if not audio_process or not audio_process.stdout:
return jsonify({'status': 'error', 'message': 'audio process not running'}), 400
sample_path = '/tmp/audio_probe.bin'
size = 0
try:
ready, _, _ = select.select([audio_process.stdout], [], [], 2.0)
if not ready:
return jsonify({'status': 'error', 'message': 'no data available'}), 504
data = audio_process.stdout.read(4096)
if not data:
return jsonify({'status': 'error', 'message': 'no data read'}), 504
with open(sample_path, 'wb') as handle:
handle.write(data)
size = len(data)
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
return jsonify({'status': 'ok', 'bytes': size})
@listening_post_bp.route('/audio/stream')