A bit of cleanup for the dummy audio before MR
This commit is contained in:
parent
87bd553bb8
commit
b84f02dd89
20
fileradio.py
20
fileradio.py
@ -4,25 +4,26 @@ import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
class FileRadio(Streamer):
|
||||
PORT = 8554
|
||||
REST_PATH = 'sample'
|
||||
|
||||
def __init__(self, path):
|
||||
super().__init__()
|
||||
self.path = path
|
||||
self.basename = os.path.basename(self.path)
|
||||
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):
|
||||
self.playback = subprocess.Popen(
|
||||
[
|
||||
'/usr/bin/ffmpeg', '-re', '-stream_loop', '-1', '-i',
|
||||
f"{self.path}", '-c', 'copy',
|
||||
'-f', 'rtsp', f"rtsp://localhost{self._stream_path()}"
|
||||
'/usr/bin/ffmpeg',
|
||||
'-re', # http://trac.ffmpeg.org/wiki/StreamingGuide#The-reflag
|
||||
'-stream_loop', '-1', # Loop the stream indefinitely
|
||||
'-i', self.path,
|
||||
'-c', 'copy',
|
||||
'-f', 'rtsp',
|
||||
self.stream_address('localhost')
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.DEVNULL,
|
||||
@ -42,6 +43,7 @@ class FileRadio(Streamer):
|
||||
self.playback = None
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
fr = FileRadio('./data/sampleaudio/taunt.mp3')
|
||||
fr.start_stream()
|
||||
|
||||
@ -194,19 +194,8 @@ def radio_info(radio):
|
||||
return str(e), 400
|
||||
|
||||
|
||||
# Youtube shit
|
||||
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')
|
||||
def start_tuuube_stream(id):
|
||||
"""Start streaming from a youtube source.
|
||||
@ -215,7 +204,13 @@ def start_tuuube_stream(id):
|
||||
---
|
||||
parameters:
|
||||
- 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
|
||||
type: string
|
||||
required: true
|
||||
|
||||
49
radio.py
49
radio.py
@ -7,19 +7,19 @@ import subprocess
|
||||
import struct
|
||||
from soapyhelpers import *
|
||||
from samplerates import *
|
||||
from streamer import Streamer, is_alive
|
||||
|
||||
|
||||
class Radio:
|
||||
class Radio(Streamer):
|
||||
REST_PATH = 'radio'
|
||||
FORMAT = 'CS16'
|
||||
SAMPLES = 8192
|
||||
PORT = 8554
|
||||
|
||||
def __init__(self, name, device_info):
|
||||
super().__init__()
|
||||
|
||||
self.name = name
|
||||
self.device_info = device_info
|
||||
self.run = False
|
||||
self.thread = None
|
||||
|
||||
self.device = soapy.Device(device_info)
|
||||
if self.device is None:
|
||||
raise RuntimeError("Failed to connect to radio device")
|
||||
@ -83,31 +83,9 @@ class Radio:
|
||||
'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):
|
||||
self._init_stream()
|
||||
|
||||
def is_alive(subprocess):
|
||||
return (subprocess.poll() is None)
|
||||
|
||||
while self.run:
|
||||
# Check that the child processes are still running
|
||||
if (not is_alive(self.demod)) or (not is_alive(self.playback)):
|
||||
@ -136,8 +114,12 @@ class Radio:
|
||||
def _init_stream(self):
|
||||
self.playback = subprocess.Popen(
|
||||
[
|
||||
'/usr/bin/ffmpeg', '-f', 's16le', '-ar', str(self.output_rate), '-ac', '2', '-i', '-',
|
||||
'-f', 'rtsp', f"rtsp://localhost{self._stream_path()}"
|
||||
'/usr/bin/ffmpeg',
|
||||
'-f', 's16le',
|
||||
'-ar', str(self.output_rate),
|
||||
'-ac', '2',
|
||||
'-i', '-',
|
||||
'-f', 'rtsp', self.stream_address()
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.DEVNULL,
|
||||
@ -145,7 +127,12 @@ class Radio:
|
||||
)
|
||||
|
||||
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,
|
||||
stdout=self.playback.stdin,
|
||||
stderr=subprocess.DEVNULL
|
||||
@ -176,8 +163,6 @@ class Radio:
|
||||
self.playback.wait()
|
||||
self.playback = None
|
||||
|
||||
def _stream_path(self):
|
||||
return f":{Radio.PORT}/radio/{self.name}"
|
||||
|
||||
|
||||
"""
|
||||
|
||||
23
streamer.py
23
streamer.py
@ -1,12 +1,29 @@
|
||||
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):
|
||||
return True if (subprocess and subprocess.poll() is None) else False
|
||||
|
||||
|
||||
class Streamer:
|
||||
PROTOCOL = 'rtsp'
|
||||
REST_PATH = 'stream'
|
||||
|
||||
def __init__(self):
|
||||
self.run = False
|
||||
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):
|
||||
return True if (self.thread and self.thread.is_alive()) else False
|
||||
@ -26,3 +43,9 @@ class Streamer:
|
||||
self.run = False
|
||||
self.thread.join()
|
||||
self.thread = None
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from pprint import pprint
|
||||
pprint(MEDIASERVER_CONFIG)
|
||||
78
tuuube.py
78
tuuube.py
@ -1,63 +1,61 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
"""
|
||||
# Install issues?
|
||||
https://stackoverflow.com/a/27481870
|
||||
|
||||
https://stackoverflow.com/a/66024755
|
||||
sudo apt purge youtube-dl
|
||||
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
|
||||
We aren't using either the apt or the pip repositories for the youtube_dl
|
||||
as there is a known bug affecting those two versions. The youtube API has changed
|
||||
since their release, causing downloads to fail.
|
||||
Make sure you use the ./setup.sh script to obtain the latest github release of
|
||||
yt_dlp, as this version carries the latest fixes.
|
||||
"""
|
||||
|
||||
#import youtube_dl
|
||||
import yt_dlp as youtube_dl
|
||||
from streamer import Streamer, is_alive
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
class Tuuube(Streamer):
|
||||
PORT = 8554
|
||||
REST_PATH = 'tuuube'
|
||||
|
||||
def __init__(self, id):
|
||||
self.id = id
|
||||
self.playback = None
|
||||
def __init__(self, name):
|
||||
super().__init__()
|
||||
self.name = name
|
||||
self.playback = None
|
||||
|
||||
def is_playing(self):
|
||||
return is_alive(self.playback)
|
||||
|
||||
def _stream_path(self):
|
||||
return f":{Tuuube.PORT}/tuuube/{self.id}"
|
||||
def source_path(self):
|
||||
return f"/tmp/{self.name}.mp3"
|
||||
|
||||
def _stream_thread(self):
|
||||
ydl_opts = {
|
||||
'format': 'bestaudio/best',
|
||||
'outtmpl': f'/tmp/{self.id}.%(ext)s',
|
||||
'postprocessors': [{
|
||||
'key': 'FFmpegExtractAudio',
|
||||
'preferredcodec': 'mp3',
|
||||
'preferredquality': '192',
|
||||
}],
|
||||
}
|
||||
if not os.path.exists(self.source_path()) or not os.path.isfile(self.source_path()):
|
||||
ydl_opts = {
|
||||
'format': 'bestaudio/best',
|
||||
'outtmpl': f'/tmp/{self.name}.%(ext)s', # yt_dlp will append %(ext) if not specified,
|
||||
'postprocessors': [{ # resulting in `/tmp/file.mp3.mp3` :/
|
||||
'key': 'FFmpegExtractAudio',
|
||||
'preferredcodec': 'mp3',
|
||||
'preferredquality': '192',
|
||||
}],
|
||||
}
|
||||
|
||||
try:
|
||||
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
|
||||
ydl.download([f'https://www.youtube.com/watch?v={self.id}'])
|
||||
except Exception as e:
|
||||
print(f'File sourcing failed, aborting stream. {e}', file=sys.stderr)
|
||||
self.run = False
|
||||
return
|
||||
try:
|
||||
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
|
||||
ydl.download([f'https://www.youtube.com/watch?v={self.name}'])
|
||||
except Exception as e:
|
||||
print(f'File sourcing failed, aborting stream. {e}', file=sys.stderr)
|
||||
self.run = False
|
||||
return
|
||||
|
||||
self.playback = subprocess.Popen(
|
||||
[
|
||||
'/usr/bin/ffmpeg', '-re', '-stream_loop', '-1', '-i',
|
||||
f"/tmp/{self.id}.mp3", '-c', 'copy',
|
||||
'-f', 'rtsp', f"rtsp://localhost{self._stream_path()}"
|
||||
'/usr/bin/ffmpeg',
|
||||
'-re',
|
||||
'-stream_loop', '-1',
|
||||
'-i', self.source_path(),
|
||||
'-c', 'copy',
|
||||
'-f', 'rtsp',
|
||||
self.stream_address('localhost')
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.DEVNULL,
|
||||
@ -65,7 +63,7 @@ class Tuuube(Streamer):
|
||||
)
|
||||
|
||||
while self.run:
|
||||
if not self.is_playing():
|
||||
if not is_alive(self.playback):
|
||||
print('Playback failed, aborting stream.', file=sys.stderr)
|
||||
break
|
||||
time.sleep(0.1)
|
||||
@ -79,9 +77,7 @@ class Tuuube(Streamer):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
#tube = Tuuube('dQw4w9WgXcQ')
|
||||
tube = Tuuube('BaW_jenozKc')
|
||||
|
||||
tube.start_stream()
|
||||
|
||||
while True:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user