Fixed RTP streaming - it is now provided by an RTSP relay MediaMTX
This commit is contained in:
parent
52c298d799
commit
d2a84a9f8f
28
dependencies/build_dependencies
vendored
28
dependencies/build_dependencies
vendored
@ -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
|
||||
|
||||
@ -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?
|
||||
|
||||
26
radio.py
26
radio.py
@ -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')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user