A bit of cleanup for the dummy audio before MR

This commit is contained in:
Jono Targett 2023-06-14 11:49:52 +09:30
parent 87bd553bb8
commit b84f02dd89
5 changed files with 96 additions and 95 deletions

View File

@ -4,25 +4,26 @@ import time
import sys import sys
import os import os
class FileRadio(Streamer): class FileRadio(Streamer):
PORT = 8554 REST_PATH = 'sample'
def __init__(self, path): def __init__(self, path):
super().__init__()
self.path = path self.path = path
self.basename = os.path.basename(self.path) self.basename = os.path.basename(self.path)
self.name, self.ext = os.path.splitext(self.basename) self.name, self.ext = os.path.splitext(self.basename)
super().__init__()
def _stream_path(self):
return f":{FileRadio.PORT}/sample/{self.name}"
def _stream_thread(self): def _stream_thread(self):
self.playback = subprocess.Popen( self.playback = subprocess.Popen(
[ [
'/usr/bin/ffmpeg', '-re', '-stream_loop', '-1', '-i', '/usr/bin/ffmpeg',
f"{self.path}", '-c', 'copy', '-re', # http://trac.ffmpeg.org/wiki/StreamingGuide#The-reflag
'-f', 'rtsp', f"rtsp://localhost{self._stream_path()}" '-stream_loop', '-1', # Loop the stream indefinitely
'-i', self.path,
'-c', 'copy',
'-f', 'rtsp',
self.stream_address('localhost')
], ],
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
@ -42,6 +43,7 @@ class FileRadio(Streamer):
self.playback = None self.playback = None
if __name__ == '__main__': if __name__ == '__main__':
fr = FileRadio('./data/sampleaudio/taunt.mp3') fr = FileRadio('./data/sampleaudio/taunt.mp3')
fr.start_stream() fr.start_stream()

View File

@ -194,19 +194,8 @@ def radio_info(radio):
return str(e), 400 return str(e), 400
# Youtube shit
tubes = {} tubes = {}
@app.route('/tuuube/report')
def tuuube_report():
"""List the tuuube streams opened and their streaming status.
---
responses:
200:
description: JSON
"""
return jsonify({id:{'active':tube.is_streaming(),'playing':tube.is_playing()} for id,tube in tubes.items()})
@app.route('/tuuube/<id>/start') @app.route('/tuuube/<id>/start')
def start_tuuube_stream(id): def start_tuuube_stream(id):
"""Start streaming from a youtube source. """Start streaming from a youtube source.
@ -215,7 +204,13 @@ def start_tuuube_stream(id):
--- ---
parameters: parameters:
- name: id - name: id
description: The youtube video ID. That is the part following the `watch?v=` in the URL. For example, dQw4w9WgXcQ. description:
The youtube video ID. That is the part following the `watch?v=` in the URL. For example, `dQw4w9WgXcQ`.\n
Other good options are - \n
`BaW_jenozKc`, yt_dlp package test video.\n
`b2je8uBlgFM`, stereo audio test.\n
`LDU_Txk06tM`, crab rave, a commonly used audio fidelity test.\n
`sPT_epMLkwQ`, Kilsyth CFA major factory fire dispatch radio traffic.
in: path in: path
type: string type: string
required: true required: true

View File

@ -7,19 +7,19 @@ import subprocess
import struct import struct
from soapyhelpers import * from soapyhelpers import *
from samplerates import * from samplerates import *
from streamer import Streamer, is_alive
class Radio: class Radio(Streamer):
REST_PATH = 'radio'
FORMAT = 'CS16' FORMAT = 'CS16'
SAMPLES = 8192 SAMPLES = 8192
PORT = 8554
def __init__(self, name, device_info): def __init__(self, name, device_info):
super().__init__()
self.name = name self.name = name
self.device_info = device_info self.device_info = device_info
self.run = False
self.thread = None
self.device = soapy.Device(device_info) self.device = soapy.Device(device_info)
if self.device is None: if self.device is None:
raise RuntimeError("Failed to connect to radio device") raise RuntimeError("Failed to connect to radio device")
@ -83,31 +83,9 @@ class Radio:
'uarts': self.device.listUARTs(), 'uarts': self.device.listUARTs(),
} }
def is_streaming(self):
return True if (self.thread and self.thread.is_alive()) else False
def start_stream(self):
if self.is_streaming():
raise RuntimeError('Stream thread is already running')
self.run = True
self.thread = Thread(target=self._stream_thread, daemon=True, args=())
self.thread.start()
def end_stream(self):
if self.thread is None:
raise RuntimeError('No stream thread to terminate')
self.run = False
self.thread.join()
self.thread = None
def _stream_thread(self): def _stream_thread(self):
self._init_stream() self._init_stream()
def is_alive(subprocess):
return (subprocess.poll() is None)
while self.run: while self.run:
# Check that the child processes are still running # Check that the child processes are still running
if (not is_alive(self.demod)) or (not is_alive(self.playback)): if (not is_alive(self.demod)) or (not is_alive(self.playback)):
@ -136,8 +114,12 @@ class Radio:
def _init_stream(self): def _init_stream(self):
self.playback = subprocess.Popen( self.playback = subprocess.Popen(
[ [
'/usr/bin/ffmpeg', '-f', 's16le', '-ar', str(self.output_rate), '-ac', '2', '-i', '-', '/usr/bin/ffmpeg',
'-f', 'rtsp', f"rtsp://localhost{self._stream_path()}" '-f', 's16le',
'-ar', str(self.output_rate),
'-ac', '2',
'-i', '-',
'-f', 'rtsp', self.stream_address()
], ],
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
@ -145,7 +127,12 @@ class Radio:
) )
self.demod = subprocess.Popen( self.demod = subprocess.Popen(
['/usr/bin/python3', 'fm_demod.py', '-f', 'CS16', '-s', str(self.sample_rate), '-d', str(self.output_rate)], [
'/usr/bin/python3', 'fm_demod.py',
'-f', 'CS16',
'-s', str(self.sample_rate),
'-d', str(self.output_rate)
],
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=self.playback.stdin, stdout=self.playback.stdin,
stderr=subprocess.DEVNULL stderr=subprocess.DEVNULL
@ -176,8 +163,6 @@ class Radio:
self.playback.wait() self.playback.wait()
self.playback = None self.playback = None
def _stream_path(self):
return f":{Radio.PORT}/radio/{self.name}"
""" """
@ -201,4 +186,4 @@ if __name__ == '__main__':
print('Ending stream...') print('Ending stream...')
sdr.end_stream() sdr.end_stream()
print('Stream ended.') print('Stream ended.')

View File

@ -1,12 +1,29 @@
from threading import Thread from threading import Thread
import yaml
with open('mediamtx.yml', 'r') as config_file:
MEDIASERVER_CONFIG = yaml.safe_load(config_file)
def is_alive(subprocess): def is_alive(subprocess):
return True if (subprocess and subprocess.poll() is None) else False return True if (subprocess and subprocess.poll() is None) else False
class Streamer: class Streamer:
PROTOCOL = 'rtsp'
REST_PATH = 'stream'
def __init__(self): def __init__(self):
self.run = False self.run = False
self.thread = None self.thread = None
self.name = None
def stream_path(self):
return f"{MEDIASERVER_CONFIG['rtspAddress']}/{type(self).REST_PATH}/{self.name}"
def stream_address(self, host):
return f"{Streamer.PROTOCOL}://{host}{self.stream_path()}"
def is_streaming(self): def is_streaming(self):
return True if (self.thread and self.thread.is_alive()) else False return True if (self.thread and self.thread.is_alive()) else False
@ -26,3 +43,9 @@ class Streamer:
self.run = False self.run = False
self.thread.join() self.thread.join()
self.thread = None self.thread = None
if __name__ == '__main__':
from pprint import pprint
pprint(MEDIASERVER_CONFIG)

View File

@ -1,63 +1,61 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
""" """
# Install issues? We aren't using either the apt or the pip repositories for the youtube_dl
https://stackoverflow.com/a/27481870 as there is a known bug affecting those two versions. The youtube API has changed
since their release, causing downloads to fail.
https://stackoverflow.com/a/66024755 Make sure you use the ./setup.sh script to obtain the latest github release of
sudo apt purge youtube-dl yt_dlp, as this version carries the latest fixes.
sudo pip3 install youtube-dl
https://stackoverflow.com/a/75504772
python3 -m pip install --force-reinstall https://github.com/yt-dlp/yt-dlp/archive/master.tar.gz
""" """
#import youtube_dl #import youtube_dl
import yt_dlp as youtube_dl import yt_dlp as youtube_dl
from streamer import Streamer, is_alive from streamer import Streamer, is_alive
import subprocess import subprocess
import time import time
import sys import sys
import os
class Tuuube(Streamer): class Tuuube(Streamer):
PORT = 8554 REST_PATH = 'tuuube'
def __init__(self, id): def __init__(self, name):
self.id = id
self.playback = None
super().__init__() super().__init__()
self.name = name
self.playback = None
def is_playing(self): def source_path(self):
return is_alive(self.playback) return f"/tmp/{self.name}.mp3"
def _stream_path(self):
return f":{Tuuube.PORT}/tuuube/{self.id}"
def _stream_thread(self): def _stream_thread(self):
ydl_opts = { if not os.path.exists(self.source_path()) or not os.path.isfile(self.source_path()):
'format': 'bestaudio/best', ydl_opts = {
'outtmpl': f'/tmp/{self.id}.%(ext)s', 'format': 'bestaudio/best',
'postprocessors': [{ 'outtmpl': f'/tmp/{self.name}.%(ext)s', # yt_dlp will append %(ext) if not specified,
'key': 'FFmpegExtractAudio', 'postprocessors': [{ # resulting in `/tmp/file.mp3.mp3` :/
'preferredcodec': 'mp3', 'key': 'FFmpegExtractAudio',
'preferredquality': '192', 'preferredcodec': 'mp3',
}], 'preferredquality': '192',
} }],
}
try: try:
with youtube_dl.YoutubeDL(ydl_opts) as ydl: with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([f'https://www.youtube.com/watch?v={self.id}']) ydl.download([f'https://www.youtube.com/watch?v={self.name}'])
except Exception as e: except Exception as e:
print(f'File sourcing failed, aborting stream. {e}', file=sys.stderr) print(f'File sourcing failed, aborting stream. {e}', file=sys.stderr)
self.run = False self.run = False
return return
self.playback = subprocess.Popen( self.playback = subprocess.Popen(
[ [
'/usr/bin/ffmpeg', '-re', '-stream_loop', '-1', '-i', '/usr/bin/ffmpeg',
f"/tmp/{self.id}.mp3", '-c', 'copy', '-re',
'-f', 'rtsp', f"rtsp://localhost{self._stream_path()}" '-stream_loop', '-1',
'-i', self.source_path(),
'-c', 'copy',
'-f', 'rtsp',
self.stream_address('localhost')
], ],
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
@ -65,7 +63,7 @@ class Tuuube(Streamer):
) )
while self.run: while self.run:
if not self.is_playing(): if not is_alive(self.playback):
print('Playback failed, aborting stream.', file=sys.stderr) print('Playback failed, aborting stream.', file=sys.stderr)
break break
time.sleep(0.1) time.sleep(0.1)
@ -79,9 +77,7 @@ class Tuuube(Streamer):
if __name__ == '__main__': if __name__ == '__main__':
#tube = Tuuube('dQw4w9WgXcQ')
tube = Tuuube('BaW_jenozKc') tube = Tuuube('BaW_jenozKc')
tube.start_stream() tube.start_stream()
while True: while True: