Fixed RTP streaming - it is now provided by an RTSP relay MediaMTX

This commit is contained in:
Jono Targett 2023-05-12 11:33:26 +09:30
parent 52c298d799
commit d2a84a9f8f
3 changed files with 74 additions and 22 deletions

View File

@ -14,12 +14,14 @@ sudo apt-get install -y \
# RedHat:
#sudo dnf install pulseaudio-libs-devel gtk3-devel freeglut-devel
# These are necessary for building the python bindings for SoapySDR.
# These must be installed before building SoapySDR for the python
# bindings to be available on the system.
sudo apt-get install -y python-dev swig
# These are required for building Soapy RTL-SDR
sudo apt-get install -y rtl-sdr librtlsdr-dev
# Build SoapySDR
if ! [ -d SoapySDR ]; then
@ -43,10 +45,21 @@ pushd SoapySDRPlay
mkdir build
pushd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
make -j
sudo make install
popd
popd
fi
# Build SoapyRTLSDR plugin
if ! [ -d SoapyRTLSDR ]; then
git clone https://github.com/pothosware/SoapyRTLSDR.git
pushd SoapyRTLSDR
mkdir build
pushd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j
sudo make install
sudo ldconfig
SoapySDRUtil --info
popd
popd
fi
@ -69,6 +82,13 @@ pushd liquid-dsp
popd
fi
# Get mediamtx (no system package available)
mkdir -p mediamtx
pushd mediamtx
wget https://github.com/aler9/mediamtx/releases/download/v0.22.2/mediamtx_v0.22.2_linux_amd64.tar.gz
tar -xzvf mediamtx_v0.22.2_linux_amd64.tar.gz
popd
echo "Setup completed." >> build.log
date >> build.log
exit 0

View File

@ -3,14 +3,16 @@
import SoapySDR as soapy
from radio import Radio
from flask import Flask, request, jsonify
from flask import Flask, jsonify
from flasgger import Swagger
app = Flask(__name__)
swag = Swagger(app)
radios = {}
@app.route('/radio/report')
def report():
"""List radio devices available to the system.
@ -48,7 +50,7 @@ def connect(radio):
for device in devices:
if radio in device.values():
try:
radios[radio] = Radio(device)
radios[radio] = Radio(radio, device)
return "", 200
except Exception as e:
radios.pop(radio)
@ -81,7 +83,6 @@ def disconnect(radio):
else:
return "Radio not connected", 400
@app.route('/radio/<radio>/configure/<frequency>')
def configure(radio, frequency):
"""Tune the radio to a frequency.
@ -95,7 +96,7 @@ def configure(radio, frequency):
type: string
required: true
- name: frequency
description: Frequency (in Hz) to tune the radio to.
description: Frequency (in Hz) to tune the radio to. Suffixing is also supported, ie 87.5M, 108.0M, etc.
in: path
type: string
required: true
@ -106,16 +107,18 @@ def configure(radio, frequency):
description: The specified radio is not connected.
"""
if radio in radios:
return jsonify(radios[radio].configure(frequency))
if radios[radio].is_streaming():
return 'no', 400
else:
return jsonify(radios[radio].configure(frequency))
else:
return "Radio not connected", 400
@app.route('/radio/<radio>/start')
def start_stream(radio):
"""Tune the radio to a frequency.
You must connect to the radio before attempting to configure it.
The radio must be configured before streaming can be started.
"""Start the radio stream.
Once the stream has been started, connect to the stream at:
rtsp://[host]:8554/radio/[radio]/stream
---
parameters:
- name: radio
@ -132,9 +135,7 @@ def start_stream(radio):
@app.route('/radio/<radio>/end')
def end_stream(radio):
"""Tune the radio to a frequency.
You must connect to the radio before attempting to configure it.
The radio must be configured before streaming can be started.
"""Terminate the radio stream.
---
parameters:
- name: radio
@ -151,8 +152,23 @@ def end_stream(radio):
if __name__ == '__main__':
import subprocess
rtsp_relay = subprocess.Popen(
[
'./dependencies/mediamtx/mediamtx',
'./dependencies/mediamtx/mediamtx.yml'
],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
app.run(
host='0.0.0.0',
threaded=True,
debug=False
)
)
print('Killing RTSP relay...')
rtsp_relay.kill()
rtsp_relay.wait() # Necessary?

View File

@ -3,7 +3,6 @@ import SoapySDR as soapy
import prefixed
from formats import *
import sys
import os
import subprocess
import struct
@ -11,9 +10,10 @@ import struct
class Radio:
FORMAT = 'CS16'
SAMPLES = 8192
PORT = 5002
PORT = 8554
def __init__(self, device_info):
def __init__(self, name, device_info):
self.name = name
self.device_info = device_info
self.run = False
self.thread = None
@ -23,6 +23,9 @@ class Radio:
raise RuntimeError("Failed to connect to radio device")
def configure(self, frequency):
if self.is_streaming():
raise RuntimeError("Cannot configure radio while a stream is active")
frequency = int(prefixed.Float(frequency))
sample_rate = 384000
bandwidth = 200000
@ -41,6 +44,9 @@ class Radio:
'gain-mode': 'auto' if self.device.getGainMode(soapy.SOAPY_SDR_RX, 0) else 'manual',
}
def is_streaming(self):
return (self.thread and self.thread.is_alive())
def start_stream(self):
if self.thread:
raise RuntimeError('Stream thread is already running')
@ -77,7 +83,10 @@ class Radio:
def _init_stream(self):
self.playback = subprocess.Popen(
['/usr/bin/ffmpeg', '-f', 's16le', '-ar', '32000', '-ac', '2', '-i', '-', '-f', 'rtp', f'rtp://0.0.0.0:{Radio.PORT}'],
[
'/usr/bin/ffmpeg', '-f', 's16le', '-ar', '32000', '-ac', '2', '-i', '-',
'-f', 'rtsp', f"rtsp://localhost:{Radio.PORT}/radio/{self.name}/stream"
],
stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
@ -100,8 +109,15 @@ class Radio:
self.device.closeStream(self.stream)
self.buffer = None
# .terminate()/.kill() followed by .wait() is required to properly clear
# killed zombie processes from the process table.
# https://stackoverflow.com/a/41961462
self.demod.kill()
self.demod.wait()
self.demod = None
self.playback.kill()
self.playback.wait()
self.playback = None
"""
@ -110,7 +126,7 @@ Quick and dirty test of the Radio class.
if __name__ == '__main__':
import time
sdr = Radio({'driver': 'sdrplay'})
sdr = Radio('demo', {'driver': 'sdrplay'})
print('Configuring...')
sdr.configure('105.5M')