9130: Extend API to support streaming dummy audio sources #1

Merged
jono merged 7 commits from 9130_dummy_audio into main 2023-06-15 12:49:14 +09:30
3 changed files with 167 additions and 3 deletions
Showing only changes of commit 9151ddce42 - Show all commits

View File

@ -2,6 +2,7 @@
import SoapySDR as soapy import SoapySDR as soapy
from radio import Radio from radio import Radio
from tuuube import Tuuube
from flask import Flask, jsonify from flask import Flask, jsonify
from flasgger import Swagger from flasgger import Swagger
@ -115,7 +116,7 @@ def configure(radio, frequency):
def start_stream(radio): def start_stream(radio):
"""Start the radio stream. """Start the radio stream.
Once the stream has been started, connect to the stream at: Once the stream has been started, connect to the stream at:
rtsp://[host]:8554/radio/[radio]/stream rtsp://[host]:8554/radio/[radio]
--- ---
parameters: parameters:
- name: radio - name: radio
@ -169,6 +170,50 @@ def radio_info(radio):
return str(e), 400 return str(e), 400
# Youtube shit
tubes = {}
@app.route('/tuuube/<id>/start')
def start_tuuube_stream(id):
"""Start streaming from a youtube source.
Once the stream has been started, connect to the stream at:
rtsp://[host]:8554/tuuube/[id]
---
parameters:
- name: id
description: The youtube video ID. That is the part following the `watch?v=` in the URL. For example, dQw4w9WgXcQ.
in: path
type: string
required: true
"""
if id not in tubes:
tubes[id] = Tuuube(id)
try:
tubes[id].start_stream()
return "", 200
except Exception as e:
return str(e), 400
@app.route('/tuuube/<id>/end')
def end_tuuube_stream(id):
"""Terminate the youtube stream.
---
parameters:
- name: id
description: The youtube video ID.
in: path
type: string
required: true
"""
try:
tubes[id].end_stream()
return "", 200
except Exception as e:
return str(e), 400
if __name__ == '__main__': if __name__ == '__main__':
import subprocess import subprocess
@ -189,10 +234,15 @@ if __name__ == '__main__':
print('Stopping any currently streaming radios...') print('Stopping any currently streaming radios...')
for radio in radios: for radio in radios:
if radios[radio].is_streaming(): if radios[radio].is_streaming():
radios[radio].end_stream() radios[radio].end_stream()
radios = None radios = None
for tube in tubes:
if tubes[tube].is_streaming():
tubes[tube].end_stream()
tubes = None
print('Killing RTSP relay...') print('Killing RTSP relay...')
rtsp_relay.kill() rtsp_relay.kill()
rtsp_relay.wait() # Necessary? rtsp_relay.wait() # Necessary?

28
streamer.py Normal file
View File

@ -0,0 +1,28 @@
from threading import Thread
def is_alive(subprocess):
return (subprocess.poll() is None)
class Streamer:
def __init__(self):
self.run = False
self.thread = None
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

86
tuuube.py Executable file
View File

@ -0,0 +1,86 @@
#! /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
"""
#import youtube_dl
import yt_dlp as youtube_dl
from streamer import Streamer, is_alive
import subprocess
import time
import sys
class Tuuube(Streamer):
PORT = 8554
def __init__(self, id):
self.id = id
super().__init__()
def _stream_path(self):
return f":{Tuuube.PORT}/tuuube/{self.id}"
def _stream_thread(self):
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': f'/tmp/{self.id}.%(ext)s',
'postprocessors': [{
'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
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()}"
],
stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
while self.run:
if not is_alive(self.playback):
print('Playback failed, aborting stream.', file=sys.stderr)
break
time.sleep(0.1)
self.run = False
self.playback.kill()
self.playback.wait()
self.playback = None
if __name__ == '__main__':
#tube = Tuuube('dQw4w9WgXcQ')
tube = Tuuube('BaW_jenozKc')
tube.start_stream()
while True:
print(tube.is_streaming())
time.sleep(1)