Compare commits
9 Commits
Author | SHA1 | Date |
---|---|---|
informatic | 232caf6ba3 | |
informatic | 268f982384 | |
informatic | 84ef5fe62c | |
informatic | 294cdf0594 | |
informatic | 267e37fc3c | |
informatic | 7752cd8071 | |
informatic | 5f06439d43 | |
informatic | 9be43c9aa7 | |
informatic | f6b84c782c |
36
Dockerfile
36
Dockerfile
|
@ -6,12 +6,12 @@ ENV SNOWMIX_VERSION 0.5.1
|
|||
WORKDIR /opt
|
||||
RUN apt update && apt -y --no-install-recommends install gstreamer1.0-tools build-essential automake \
|
||||
autoconf libtool g++ pkg-config libsdl1.2-dev libpango1.0-dev \
|
||||
libpng-dev libosmesa6-dev freeglut3-dev wget ca-certificates
|
||||
libpng-dev libosmesa6-dev freeglut3-dev wget ca-certificates \
|
||||
\
|
||||
tcl tk bwidget tcl-dev
|
||||
RUN wget https://downloads.sourceforge.net/project/snowmix/Snowmix-${SNOWMIX_VERSION}.tar.gz -O Snowmix.tgz && \
|
||||
tar xvf Snowmix.tgz
|
||||
|
||||
RUN apt -y --no-install-recommends install tcl tk bwidget tcl-dev
|
||||
|
||||
RUN cd Snowmix-${SNOWMIX_VERSION} && \
|
||||
aclocal && autoconf && libtoolize --force && automake --add-missing && \
|
||||
./configure && make && make install
|
||||
|
@ -20,30 +20,40 @@ ENV SNOWMIX /usr/local/lib/Snowmix-${SNOWMIX_VERSION}
|
|||
|
||||
RUN useradd snowmix && mkdir /home/snowmix /run/snowmix && chown snowmix /home/snowmix /run/snowmix
|
||||
|
||||
WORKDIR /config
|
||||
|
||||
RUN apt install -y --no-install-recommends \
|
||||
RUN apt update && apt install -y --no-install-recommends \
|
||||
gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \
|
||||
gstreamer1.0-plugins-base gstreamer1.0-x gstreamer1.0-pulseaudio \
|
||||
netcat bc golang git libpcap-dev
|
||||
gstreamer1.0-plugins-base gstreamer1.0-x gstreamer1.0-pulseaudio gstreamer1.0-libav gstreamer1.0-vaapi \
|
||||
netcat bc golang git libpcap-dev \
|
||||
\
|
||||
python-pyparsing python-gst-1.0 libgstreamer-plugins-base1.0-dev libglib2.0-dev \
|
||||
libjson-glib-dev libreadline-dev libncursesw5-dev libdaemon-dev libjansson-dev gtk-doc-tools \
|
||||
\
|
||||
libgstreamer1.0-dev python-gi-dev python-dev
|
||||
|
||||
# Patch netcat!
|
||||
RUN sed -i -e 's_NC\=.*_NC="nc -q1"_' /usr/local/lib/Snowmix-0.5.1/scripts/snowmix-settings
|
||||
|
||||
RUN apt install -y --no-install-recommends libgstreamer1.0-dev python-gi-dev python-dev
|
||||
RUN git clone git://anongit.freedesktop.org/git/gstreamer/gst-python -b 1.14.1 /opt/gst-python && \
|
||||
cd /opt/gst-python && \
|
||||
./autogen.sh --disable-gtk-doc --noconfigure && \
|
||||
./configure --with-libpython-dir="/usr/lib/x86_64-linux-gnu" && \
|
||||
make && \
|
||||
make install
|
||||
make install && rm -rf /opt/gst-python
|
||||
|
||||
RUN apt install -y --no-install-recommends python-pyparsing python-gst-1.0
|
||||
RUN git clone https://github.com/RidgeRun/gstd-1.x.git /opt/gstd && cd /opt/gstd && ./autogen.sh && ./configure && make && make install && rm -rf /opt/gstd
|
||||
RUN git clone https://github.com/RidgeRun/gst-interpipe.git /opt/gst-interpipe && cd /opt/gst-interpipe && ./autogen.sh --noconfigure && ./configure --libdir /usr/lib/x86_64-linux-gnu/gstreamer-1.0/ --disable-gtk-doc && make && make install && rm -rf /opt/gst-interpipe
|
||||
|
||||
ADD ./tools /tools
|
||||
ADD ./gst-snowmix /opt/gst-snowmix
|
||||
RUN rm /opt/gst-snowmix/python/snowmix.py
|
||||
ADD ./api/snowmix.py /opt/gst-snowmix/python
|
||||
ENV GST_PLUGIN_PATH $GST_PLUGIN_PATH:/usr/local/lib/gstreamer-1.0:/opt/gst-snowmix
|
||||
|
||||
ENV GOPATH /usr/src/go
|
||||
RUN mkdir $GOPATH && cd /tools/de-ip-hdmi && go get -d . && go build . && chmod +s /tools/de-ip-hdmi/de-ip-hdmi
|
||||
ENV GST_PLUGIN_PATH $GST_PLUGIN_PATH:/opt/gst-snowmix
|
||||
RUN mkdir $GOPATH && cd /tools/de-ip-hdmi && go get -d . && go build . && chmod +s /tools/de-ip-hdmi/de-ip-hdmi && rm -rf $GOPATH
|
||||
|
||||
RUN wget https://raw.githubusercontent.com/vishnubob/wait-for-it/54d1f0bfeb6557adf8a3204455389d0901652242/wait-for-it.sh -O /usr/local/bin/wait-for-it && chmod +x /usr/local/bin/wait-for-it
|
||||
|
||||
USER snowmix
|
||||
WORKDIR /config
|
||||
CMD [ "/tools/run-snowmix" ]
|
||||
|
|
|
@ -2,6 +2,7 @@ import socket
|
|||
import re
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
|
||||
from pyparsing import nestedExpr, originalTextFor
|
||||
|
||||
|
@ -66,7 +67,7 @@ class SnowmixClient(object):
|
|||
self.fd.write(command + '\r\n')
|
||||
self.fd.flush()
|
||||
|
||||
while True:
|
||||
while expect:
|
||||
line = self.fd.readline()
|
||||
|
||||
if not line:
|
||||
|
@ -201,6 +202,27 @@ class SnowmixClient(object):
|
|||
for line in self.call('audio %s channels' % (audio_type), 'STAT:')
|
||||
}
|
||||
|
||||
def audio_volume(self, audio_id, volume_l, volume_r=None, audio_type='feed'):
|
||||
if volume_r is None:
|
||||
volume_r = volume_l
|
||||
|
||||
return next(self.call('audio %s volume %s %f %f' % (audio_type, audio_id, volume_l, volume_r), None), None)
|
||||
|
||||
def audio_mute(self, audio_id, mute, audio_type='feed'):
|
||||
mute = 'on' if mute else 'off'
|
||||
|
||||
return next(self.call('audio %s mute %s %s' % (audio_type, mute, audio_id), None), None)
|
||||
|
||||
def audio_status(self, audio_type='feed'):
|
||||
return dict(parse_table(list((self.call('audio %s status' % (audio_type,), 'STAT:')))))
|
||||
|
||||
def parse_table(lines):
|
||||
keys = lines[0].split(' : ')[1].split(' ')
|
||||
for line in lines[1:]:
|
||||
id_, _, values = line.partition(' : ')
|
||||
yield (id_.split(' ')[-1], dict(zip(keys, values.split(' '))))
|
||||
#return [dict(zip(keys, l.split(' : ')[1].split(' '))) for l in lines[1:]]
|
||||
|
||||
if __name__ == "__main__":
|
||||
c = SnowmixClient('snowmix')
|
||||
|
||||
|
|
|
@ -88,22 +88,32 @@ tcl eval SceneSetBackground 0 1
|
|||
tcl eval SceneCreate "Fullscreen 1" 1
|
||||
#tcl eval SceneAddFrame 1 1 0 0 1280 720
|
||||
tcl eval SceneAddFrame 1 2 1410 20 480 270
|
||||
tcl eval SceneSetFrameSource 1 1 feed 2 0 1
|
||||
tcl eval SceneSetFrameSource 1 1 feed 1 1 1
|
||||
tcl eval SceneSetFrameSource 1 1 feed 2 0 1
|
||||
tcl eval SceneSetFrameSource 1 2 feed 2 1 1
|
||||
tcl eval SceneSetFrameActive 1 2 0 1
|
||||
tcl eval SceneSetBackground 1 1
|
||||
# Disable background on fullscreen scenes
|
||||
tcl eval SceneAlphaLink 1 -2 0
|
||||
tcl eval SceneAlpha 1 -2 0
|
||||
|
||||
|
||||
# Scene 2
|
||||
tcl eval SceneCreate "Fullscreen 2" 2
|
||||
tcl eval SceneAddFrame 2 1 0 0 1920 1080
|
||||
tcl eval SceneAddFrame 2 2 1410 20 480 270
|
||||
tcl eval SceneSetFrameSource 2 1 feed 2 0 1
|
||||
tcl eval SceneSetFrameSource 2 1 feed 1 1 1
|
||||
tcl eval SceneSetFrameSource 2 2 feed 2 1 1
|
||||
tcl eval SceneSetFrameSource 2 1 feed 2 1 1
|
||||
tcl eval SceneSetFrameSource 2 2 feed 1 1 1
|
||||
tcl eval SceneSetBackground 2 1
|
||||
# Disable background on fullscreen scenes
|
||||
tcl eval SceneAlphaLink 2 -2 0
|
||||
tcl eval SceneAlpha 2 -2 0
|
||||
|
||||
tcl eval SceneSetState 0 1
|
||||
|
||||
# Clean fade between scenes (no FTB)
|
||||
tcl eval SceneFadeSpeed pause all - 0
|
||||
|
||||
include ini/streaming-audio
|
||||
|
||||
stack 0
|
||||
|
|
|
@ -6,8 +6,17 @@ x-defaults: &defaults
|
|||
working_dir: /tools
|
||||
volumes:
|
||||
- sockets:/run/snowmix
|
||||
- ./tools:/tools:ro
|
||||
- ./config:/config:ro
|
||||
- ./assets:/assets:ro
|
||||
- /storage:/storage
|
||||
- /tmp/.X11-unix:/tmp/.X11-unix
|
||||
environment:
|
||||
- DISPLAY=${DISPLAY:-:0}
|
||||
network_mode: host
|
||||
ipc: host
|
||||
extra_hosts:
|
||||
- "janus:172.17.0.1"
|
||||
|
||||
services:
|
||||
snowmix:
|
||||
|
@ -34,12 +43,22 @@ services:
|
|||
- 8005:8005/udp
|
||||
network_mode: host
|
||||
|
||||
gstd:
|
||||
<<: *defaults
|
||||
command: gstd -D --gst-debug-level=4
|
||||
|
||||
gstd-init:
|
||||
<<: *defaults
|
||||
command: wait-for-it localhost:5000 -- gstd-client source init.gstd
|
||||
restart: "no"
|
||||
|
||||
janus-feed:
|
||||
build: .
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- sockets:/run/snowmix
|
||||
- ./tools:/tools:ro
|
||||
- ./gst-snowmix:/opt/gst-snowmix
|
||||
- /storage:/storage
|
||||
environment:
|
||||
- SNOWMIX_YOUTUBE_SECRET
|
||||
|
@ -63,7 +82,7 @@ services:
|
|||
build: api
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 5000:5000
|
||||
- 5001:5000
|
||||
volumes:
|
||||
- ./api:/app
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- Mode: Python -*-
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
|
||||
import gi
|
||||
gi.require_version('GstBase', '1.0')
|
||||
|
||||
from gi.repository import Gst, GObject, GstBase
|
||||
Gst.init(None)
|
||||
|
||||
class SnowmixAudioSrc(Gst.Pipeline):
|
||||
__gstmetadata__ = ('testbin Python','Transform', \
|
||||
'Simple testbin element written in python', 'Marianna S. Buschle')
|
||||
|
||||
__gsttemplates__ = (Gst.PadTemplate.new("src",
|
||||
Gst.PadDirection.SRC,
|
||||
Gst.PadPresence.ALWAYS,
|
||||
Gst.Caps.new_any()))
|
||||
|
||||
# Doesn't work either...
|
||||
#ip = GObject.Property(type=str, default='127.0.0.1')
|
||||
#port = GObject.Property(type=int, default=9999)
|
||||
|
||||
__gproperties__ = {
|
||||
"window-duration": (float,
|
||||
"Window Duration",
|
||||
"Duration of the sliding window, in seconds",
|
||||
0.01,
|
||||
100.0,
|
||||
5,
|
||||
GObject.ParamFlags.READWRITE
|
||||
),
|
||||
"test-window-duration": (float,
|
||||
"Window Duration",
|
||||
"Duration of the sliding window, in seconds",
|
||||
0.01,
|
||||
100.0,
|
||||
5,
|
||||
GObject.ParamFlags.READWRITE
|
||||
)
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
Gst.Bin.__init__(self)
|
||||
Gst.info('hello xD')
|
||||
#print(self.ip)
|
||||
#print(self.port)
|
||||
testsrc = Gst.ElementFactory.make("audiotestsrc", "audio_source")
|
||||
self.add(testsrc)
|
||||
self.add_pad(Gst.GhostPad.new("src", list(testsrc.iterate_src_pads())[0]))
|
||||
|
||||
self.window_duration = 5
|
||||
|
||||
|
||||
def do_get_property(self, prop):
|
||||
print('get',prop.name)
|
||||
if prop.name.endswith('window-duration'):
|
||||
return self.window_duration
|
||||
else:
|
||||
raise AttributeError('unknown property %s' % prop.name)
|
||||
|
||||
def do_set_property(self, prop, value):
|
||||
print('set',prop.name)
|
||||
if prop.name == 'window-duration':
|
||||
self.window_duration = value
|
||||
else:
|
||||
raise AttributeError('unknown property %s' % prop.name)
|
||||
|
||||
GObject.type_register(SnowmixAudioSrc)
|
||||
__gstelementfactory__ = ("snowmixaudiosrc", Gst.Rank.NONE, SnowmixAudioSrc)
|
|
@ -0,0 +1,107 @@
|
|||
import socket
|
||||
import json
|
||||
import re
|
||||
from flask import Flask, render_template, redirect, flash, request, url_for
|
||||
|
||||
|
||||
class GSTDException(Exception):
|
||||
pass
|
||||
|
||||
class GSTDClient(object):
|
||||
def __init__(self, addr='127.0.0.1', port=5000):
|
||||
self.addr = addr
|
||||
self.port = port
|
||||
|
||||
def connect(self):
|
||||
self.sock = socket.socket()
|
||||
self.sock.connect((self.addr, self.port))
|
||||
|
||||
def request(self, cmd, args=None):
|
||||
if args:
|
||||
cmd += ' ' + args
|
||||
print('<', cmd)
|
||||
self.sock.send(cmd.encode())
|
||||
resp = self.recv_object()
|
||||
print('>', resp)
|
||||
if resp['code'] != 0:
|
||||
raise GSTDException(resp)
|
||||
return resp['response']
|
||||
|
||||
def recv_object(self):
|
||||
buf = ''
|
||||
decoder = json.JSONDecoder()
|
||||
|
||||
while True:
|
||||
buf += self.sock.recv(4096).decode().replace('],{', '],') # wat
|
||||
try:
|
||||
obj, end = decoder.raw_decode(buf, 0)
|
||||
return obj
|
||||
except ValueError as exc:
|
||||
print(exc)
|
||||
pass
|
||||
|
||||
def pipelines_state(self):
|
||||
return {
|
||||
p['name']: self.request('read', '/pipelines/%s/state' % (p['name'],))['value']
|
||||
for p in self.request('list_pipelines')['nodes']
|
||||
}
|
||||
|
||||
def pipeline_elements(self, name):
|
||||
elements = self.request('read', '/pipelines/%s/elements' % (name,))['nodes']
|
||||
|
||||
return {
|
||||
e['name']: self.request('read', '/pipelines/%s/elements/%s' % (name, e['name']))['element_properties']
|
||||
for e in elements
|
||||
}
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
||||
app.config['SECRET_KEY'] = 'changeme'
|
||||
|
||||
def parse_access(access):
|
||||
return int(re.findall('\d+', access)[0])
|
||||
|
||||
|
||||
@app.template_filter()
|
||||
def access_writable(access):
|
||||
return bool(parse_access(access) & 2)
|
||||
|
||||
|
||||
def gstd():
|
||||
c = GSTDClient()
|
||||
c.connect()
|
||||
return c
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
c = gstd()
|
||||
return render_template('index.html', pipelines=c.pipelines_state())
|
||||
|
||||
|
||||
@app.route('/pipelines/<name>/edit')
|
||||
def pipeline_details(name):
|
||||
c = gstd()
|
||||
return render_template('pipeline_details.html',
|
||||
elements=c.pipeline_elements(name), pipeline=name)
|
||||
|
||||
|
||||
@app.route('/pipelines/<pipeline>/edit/<element>/<prop>')
|
||||
def element_edit(pipeline, element, prop):
|
||||
c = gstd()
|
||||
try:
|
||||
c.request('element_set', ' '.join([pipeline, element, prop, request.args.get('value', '')]))
|
||||
except GSTDException as exc:
|
||||
flash(exc.args[0]['description'], 'danger')
|
||||
return redirect(url_for('pipeline_details', name=pipeline))
|
||||
|
||||
|
||||
@app.route('/pipelines/<name>/<action>')
|
||||
def pipeline_action(name, action):
|
||||
c = gstd()
|
||||
try:
|
||||
c.request('pipeline_%s' % action, name)
|
||||
except GSTDException as exc:
|
||||
flash(exc.args[0]['description'], 'danger')
|
||||
return redirect('/')
|
|
@ -0,0 +1,29 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Starter Template for Bootstrap</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic-bootstrap.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }}" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,30 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<table class="table table-hover table-striped">
|
||||
<thead><tr>
|
||||
<th>Name</th>
|
||||
<th>State</th>
|
||||
<th>Actions</th>
|
||||
</tr></thead>
|
||||
{% for pipeline, state in pipelines.items() %}
|
||||
<tr>
|
||||
<td><code>{{ pipeline }}</code></td>
|
||||
{% set state_colors = {'PLAYING': 'success', 'PAUSED': 'warning', 'NULL': 'danger'} %}
|
||||
<td><span class="badge badge-{{ state_colors[state] }}">{{ state }}</span></td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
{% if state == 'PLAYING' %}
|
||||
<a href="{{ url_for('pipeline_action', name=pipeline, action='pause') }}" class="btn btn-sm btn-warning"><span class="oi oi-media-pause" title="pause" aria-hidden="true"></span></a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('pipeline_action', name=pipeline, action='play') }}" class="btn btn-sm btn-success"><span class="oi oi-media-play" title="play" aria-hidden="true"></span></a>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('pipeline_action', name=pipeline, action='stop') }}" class="btn btn-sm btn-danger"><span class="oi oi-media-stop" title="stop" aria-hidden="true"></span></a>
|
||||
<a href="{{ url_for('pipeline_action', name=pipeline, action='edit') }}" class="btn btn-sm btn-info"><span class="oi oi-pencil" title="edit" aria-hidden="true"></span></a>
|
||||
<a href="{{ url_for('pipeline_action', name=pipeline, action='delete') }}" class="btn btn-sm btn-danger"><span class="oi oi-x" title="delete" aria-hidden="true"></span></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
|
@ -0,0 +1,29 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<table>
|
||||
{% for element, properties in elements.items() %}
|
||||
<tr><th colspan=3>
|
||||
{{ element }}
|
||||
</th></tr>
|
||||
|
||||
{% for prop in properties %}
|
||||
<tr>
|
||||
<td>{{ prop.name }}</td>
|
||||
<td>
|
||||
{% if prop.param.access|access_writable %}
|
||||
<form action="{{ url_for('element_edit', pipeline=pipeline, element=element, prop=prop.name) }}">
|
||||
<div class="input-group">
|
||||
<input type="text" name="value" value="{{ prop.value }}" class="form-control" />
|
||||
<div class="input-group-append"><button class="btn btn-outline-secondary">Save</button></div>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
{{ prop.value }}
|
||||
{% endif %}
|
||||
<small>{{ prop.param.description }}</small>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
|
@ -0,0 +1,84 @@
|
|||
"""Small example OSC server
|
||||
|
||||
This program listens to several addresses, and prints some information about
|
||||
received packets.
|
||||
"""
|
||||
import argparse
|
||||
import math
|
||||
import threading
|
||||
import time
|
||||
|
||||
from pythonosc import dispatcher
|
||||
from pythonosc import osc_server
|
||||
from pythonosc import udp_client
|
||||
|
||||
from snowmix import SnowmixClient
|
||||
|
||||
def snowmix_wrap(func, **kwargs):
|
||||
def wrapped(addr, *args):
|
||||
print(func, addr, *args)
|
||||
return func(*args)
|
||||
return wrapped
|
||||
|
||||
|
||||
class PollerThread(threading.Thread):
|
||||
daemon = True
|
||||
def run(self):
|
||||
while True:
|
||||
try:
|
||||
for scene_id, state in self.client.tcl('SceneSetState'):
|
||||
print(scene_id, state)
|
||||
self.osc.send_message('/scene/state', [scene_id, float(state)])
|
||||
|
||||
for feed_id, status in self.client.audio_status().items():
|
||||
#print(status)
|
||||
self.osc.send_message('/feed/state', [feed_id, status['state']])
|
||||
for i, v in enumerate(status['rms'].split(',')):
|
||||
#msg = '/feed/%s/rms_%s' % (feed_id, ['l', 'r'][i])
|
||||
#print(msg, float(v))
|
||||
#self.osc.send_message(msg, float(v))
|
||||
msg = '/feed/rms_%s' % (['l', 'r'][i],)
|
||||
self.osc.send_message(msg, [feed_id, float(v)])
|
||||
|
||||
#print(list(self.client.call('audio feed status', 'STAT:'))[1:])
|
||||
#for s in self.client.scene_list():
|
||||
# print(self.client.scene_info(s))
|
||||
except Exception as exc:
|
||||
print(exc)
|
||||
pass
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--ip",
|
||||
default="127.0.0.1", help="The ip to listen on")
|
||||
parser.add_argument("--port",
|
||||
type=int, default=5005, help="The port to listen on")
|
||||
parser.add_argument("--status-ip",
|
||||
default="255.255.255.255", help="IP to report status on")
|
||||
parser.add_argument("--status-port",
|
||||
type=int, default=8080, help="The port to report status on")
|
||||
parser.add_argument("--snowmix-ip",
|
||||
default="10.8.0.95", help="Snowmix server IP")
|
||||
parser.add_argument("--snowmix-port", type=int, default=9999, help="Snowmix port")
|
||||
args = parser.parse_args()
|
||||
|
||||
s = SnowmixClient(args.snowmix_ip, args.snowmix_port)
|
||||
dispatcher = dispatcher.Dispatcher()
|
||||
|
||||
#dispatcher.map("/filter", print)
|
||||
dispatcher.map("/feed/volume", snowmix_wrap(s.audio_volume))
|
||||
dispatcher.map("/feed/mute", snowmix_wrap(s.audio_mute))
|
||||
dispatcher.map("/scene/fade", snowmix_wrap(s.scene_fade))
|
||||
dispatcher.map("/scene/cut", snowmix_wrap(s.scene_cut))
|
||||
|
||||
poller = PollerThread()
|
||||
poller.osc = udp_client.SimpleUDPClient(args.status_ip, args.status_port, True)
|
||||
poller.client = s
|
||||
poller.start()
|
||||
|
||||
server = osc_server.ThreadingOSCUDPServer(
|
||||
(args.ip, args.port), dispatcher)
|
||||
print("Serving on {}".format(server.server_address))
|
||||
server.serve_forever()
|
|
@ -0,0 +1 @@
|
|||
../api/snowmix.py
|
|
@ -0,0 +1,5 @@
|
|||
#source preview.gstd
|
||||
source snowmix.gstd
|
||||
source mkv.gstd
|
||||
|
||||
pipeline_play shortpipe
|
|
@ -0,0 +1,37 @@
|
|||
pipeline_delete mp3_enc
|
||||
pipeline_create mp3_enc interpipesrc listen-to=audio_out is-live=true enable-sync=true format=3 ! queue ! audioconvert ! audioresample ! audiorate ! queue ! lamemp3enc bitrate=128 ! interpipesink name=mp3_enc forward-events=true
|
||||
# ! matroskamux streamable=true ! tcpserversink port=2138 host=0.0.0.0
|
||||
#pipeline_play mp3_enc
|
||||
|
||||
|
||||
pipeline_delete h264_enc
|
||||
pipeline_create h264_enc interpipesrc format=3 listen-to=video_out is-live=true enable-sync=true ! queue ! videoconvert ! video/x-raw,format=I420 ! queue ! x264enc bitrate=5000 key-int-max=30 bframes=0 byte-stream=false aud=true tune=zerolatency speed-preset=ultrafast ! h264parse ! interpipesink name=h264_out forward-events=true
|
||||
#pipeline_play h264_enc
|
||||
|
||||
pipeline_delete h264_hi_enc
|
||||
pipeline_create h264_hi_enc interpipesrc format=3 listen-to=video_out is-live=true enable-sync=true ! queue ! videoconvert ! queue ! x264enc bitrate=15000 key-int-max=30 bframes=0 byte-stream=false aud=true tune=zerolatency speed-preset=ultrafast ! h264parse ! interpipesink name=h264_hi_out forward-events=true
|
||||
|
||||
pipeline_delete flv
|
||||
pipeline_create flv interpipesrc format=3 listen-to=h264_out is-live=true enable-sync=true allow-renegotiation=true accept-events=true ! queue ! flvmux streamable=true name=flvmux ! queue ! tcpserversink host=0.0.0.0 port=2137 sync=false async=false interpipesrc listen-to=mp3_enc is-live=true enable-sync=true allow-renegotiation=true accept-events=true ! queue ! mpegaudioparse ! audio/mpeg,rate=44100,channels=2 ! queue ! flvmux.
|
||||
|
||||
pipeline_delete flv_record
|
||||
pipeline_create flv_record interpipesrc format=3 listen-to=h264_hi_out is-live=true enable-sync=true allow-renegotiation=true accept-events=true ! queue ! flvmux streamable=true name=flvmux ! queue ! filesink location=/tmp/recording.mkv sync=false async=false interpipesrc listen-to=mp3_enc is-live=true enable-sync=true allow-renegotiation=true accept-events=true ! queue ! mpegaudioparse ! audio/mpeg,rate=44100,channels=2 ! queue ! flvmux.
|
||||
|
||||
pipeline_delete mkv
|
||||
pipeline_create mkv interpipesrc format=3 listen-to=h264_out is-live=true allow-renegotiation=true enable-sync=true accept-events=true ! h264parse ! queue ! matroskamux streamable=true name=mkvmux ! queue ! tcpserversink host=0.0.0.0 port=2138 sync=false async=false interpipesrc enable-sync=false listen-to=mp3_enc is-live=true allow-renegotiation=true accept-events=true ! queue ! mpegaudioparse ! audio/mpeg,rate=44100,channels=2 ! queue ! mkvmux.
|
||||
|
||||
pipeline_delete mkv_record
|
||||
pipeline_create mkv_record interpipesrc format=3 listen-to=h264_hi_out is-live=true enable-sync=true allow-renegotiation=true accept-events=true ! queue ! matroskamux streamable=true name=mkvmux ! queue ! filesink location=/tmp/recording.mkv sync=false async=false interpipesrc listen-to=mp3_enc is-live=true enable-sync=true allow-renegotiation=true accept-events=true ! queue ! mpegaudioparse ! audio/mpeg,rate=44100,channels=2 ! queue ! mkvmux.
|
||||
|
||||
pipeline_delete shortpipe
|
||||
#pipeline_create shortpipe interpipesrc format=3 listen-to=video_out is-live=true do-timestamp=true ! queue ! videoconvert ! queue ! x264enc bitrate=5000 key-int-max=30 bframes=0 byte-stream=false aud=true tune=zerolatency speed-preset=ultrafast threads=2 ! video/x-h264,profile=high ! tee name=encvid ! queue ! matroskamux streamable=true name=flvmux ! queue ! tcpserversink host=0.0.0.0 port=2137 sync=false async=false interpipesrc listen-to=audio_out is-live=true format=3 ! queue ! audioconvert ! audioresample ! audiorate ! queue ! lamemp3enc bitrate=128 ! queue ! mpegaudioparse ! queue ! audio/mpeg,rate=44100,channels=2 ! queue ! mpegaudioparse ! flvmux. encvid. ! queue ! rtph264pay config-interval=1 pt=96 ! udpsink host=janus port=8004
|
||||
|
||||
pipeline_delete interpipeshort
|
||||
#pipeline_create interpipeshort interpipesrc listen-to=audio_out is-live=true enable-sync=true format=3 ! queue ! audioconvert ! audioresample ! audiorate ! queue ! lamemp3enc bitrate=128 ! interpipesink name=mp3_enc forward-events=true interpipesrc format=3 listen-to=h264_out is-live=true enable-sync=true allow-renegotiation=true accept-events=true ! queue ! flvmux streamable=true name=flvmux ! queue ! tcpserversink host=0.0.0.0 port=2137 sync=false async=false interpipesrc listen-to=mp3_enc is-live=true enable-sync=true allow-renegotiation=true accept-events=true ! queue ! mpegaudioparse ! audio/mpeg,rate=44100,channels=2 ! queue ! mkvmux.
|
||||
|
||||
#pipeline_play interpipeshort
|
||||
|
||||
pipeline_play mkv
|
||||
pipeline_play flv
|
||||
pipeline_play h264_enc
|
||||
pipeline_play mp3_enc
|
|
@ -0,0 +1,3 @@
|
|||
pipeline_delete playback
|
||||
pipeline_create playback playbin uri=file:///assets/Sync-Footage-V1-H264.mp4 audio-sink=snowmixaudiosink video-sink="videoconvert ! videorate ! queue ! video/x-raw,format=BGRA,pixel-aspect-ratio=1/1,interlace-mode=progressive,width=1920,height=1080,framerate=30/1 ! queue ! shmsink socket-path=/run/snowmix/feed1-control-pipe shm-size=165888000 wait-for-connection=0 sync=true"
|
||||
pipeline_play playback
|
|
@ -0,0 +1,3 @@
|
|||
pipeline_delete preview
|
||||
pipeline_create preview interpipesrc listen-to=video_out is_live=true ! videoconvert ! timeoverlay ! queue ! taginject tags="title=screen:DisplayPort-1:snowmix" ! ximagesink
|
||||
pipeline_play preview
|
|
@ -0,0 +1,10 @@
|
|||
pipeline_delete video_out
|
||||
pipeline_create video_out shmsrc socket-path=/run/snowmix/mixer1 do-timestamp=true is-live=true ! video/x-raw,format=(string)BGRA,pixel-aspect-ratio=(fraction)1/1,interlace-mode=(string)progressive,framerate=30/1,width=1920,height=1080 ! queue ! interpipesink name=video_out forward-events=true
|
||||
pipeline_play video_out
|
||||
|
||||
pipeline_delete audio_out
|
||||
pipeline_create audio_out snowmixaudiosrc ! queue ! interpipesink name=audio_out forward-events=true
|
||||
pipeline_play audio_out
|
||||
|
||||
pipeline_delete video_mirror
|
||||
pipeline_create video_mirror interpipesrc format=3 listen-to=video_out is-live=true enable-sync=true ! queue ! shmsink socket-path=/run/snowmix/mixer1-mirror wait-for-connection=false shm-size=165888000 sync=false async=false
|
Loading…
Reference in New Issue