WIP: gst-snowmix plugins set
parent
8993042ac0
commit
7407e7bd45
|
@ -44,6 +44,9 @@ class SnowmixClient(object):
|
|||
self.fd = self.sock.makefile('rw')
|
||||
self.version = self.fd.readline().split(' ')[2]
|
||||
|
||||
def close(self):
|
||||
self.sock.close()
|
||||
|
||||
def flush_input(self):
|
||||
self.sock.setblocking(0)
|
||||
|
||||
|
@ -186,6 +189,18 @@ class SnowmixClient(object):
|
|||
print(scene_id, frame_id, active)
|
||||
print(next(self.call('tcl eval SceneSetFrameActive %d %d %d 0' % (scene_id, frame_id, active))))
|
||||
|
||||
def audio_rate(self, audio_type='feed'):
|
||||
return {
|
||||
int(line.split(' ')[2]): int(line.split(' ')[4])
|
||||
for line in self.call('audio %s rate' % (audio_type), 'STAT:')
|
||||
}
|
||||
|
||||
def audio_channels(self, audio_type='feed'):
|
||||
return {
|
||||
int(line.split(' ')[2]): int(line.split(' ')[4])
|
||||
for line in self.call('audio %s channels' % (audio_type), 'STAT:')
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
c = SnowmixClient('snowmix')
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
gst-snowmix
|
||||
===========
|
||||
|
||||
Just a bunch of Gstreamer elements to interact with snowmix
|
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- Mode: Python -*-
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
|
||||
# sinkelement.py
|
||||
# (c) 2005 Edward Hervey <edward@fluendo.com>
|
||||
# (c) 2007 Jan Schmidt <jan@fluendo.com>
|
||||
# Licensed under LGPL
|
||||
#
|
||||
# Small test application to show how to write a sink element
|
||||
# in 20 lines in python and place into the gstreamer registry
|
||||
# so it can be autoplugged or used from parse_launch.
|
||||
#
|
||||
# You can run the example from the source doing from gst-python/:
|
||||
#
|
||||
# $ export GST_PLUGIN_PATH=$GST_PLUGIN_PATH:$PWD/plugin:$PWD/examples/plugins
|
||||
# $ GST_DEBUG=python:4 gst-launch-1.0 fakesrc num-buffers=10 ! mysink
|
||||
|
||||
|
||||
# inf notes 20190221
|
||||
# https://gist.github.com/jackersson/9d3b0c578c1e625b6b79ea04e2cebd15
|
||||
# https://mathieuduponchelle.github.io/2018-02-01-Python-Elements.html?gi-language=undefined
|
||||
import os
|
||||
import logging
|
||||
from snowmix import SnowmixClient
|
||||
|
||||
import gi
|
||||
gi.require_version('GstBase', '1.0')
|
||||
|
||||
from gi.repository import Gst, GObject, GstBase, Gio
|
||||
Gst.init(None)
|
||||
#
|
||||
# Simple Sink element created entirely in python
|
||||
#
|
||||
class SnowmixAudioSink(Gst.Bin):
|
||||
__gstmetadata__ = ('CustomSink','Sink', \
|
||||
'Custom test sink element', 'Edward Hervey')
|
||||
|
||||
__gsttemplates__ = Gst.PadTemplate.new("sink",
|
||||
Gst.PadDirection.SINK,
|
||||
Gst.PadPresence.ALWAYS,
|
||||
Gst.Caps.new_any())
|
||||
|
||||
__gproperties__ = {
|
||||
"feed": (int,
|
||||
"Feed",
|
||||
"Target feed number",
|
||||
1,
|
||||
255,
|
||||
1,
|
||||
GObject.ParamFlags.READWRITE
|
||||
),
|
||||
#"host": (str,
|
||||
# "Snowmix Host",
|
||||
# "Target snowmix host",
|
||||
# '',
|
||||
# GObject.ParamFlags.READWRITE
|
||||
# ),
|
||||
"port": (int,
|
||||
"Snowmix Port",
|
||||
"Target snowmix port",
|
||||
0,
|
||||
65535,
|
||||
9999,
|
||||
GObject.ParamFlags.READWRITE
|
||||
),
|
||||
# [...]
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
Gst.info('init(%r, %r)' % (args, kwargs))
|
||||
super(SnowmixAudioSink, self).__init__(*args, **kwargs)
|
||||
self.caps = caps = Gst.ElementFactory.make("capsfilter", None)
|
||||
|
||||
self.sink = sink = Gst.ElementFactory.make("multisocketsink", None)
|
||||
self.add(sink)
|
||||
self.add(caps)
|
||||
|
||||
# Link from left to right
|
||||
caps.link(sink)
|
||||
|
||||
# Expose first/last
|
||||
self.add_pad(Gst.GhostPad.new("sink", caps.get_static_pad("sink")))
|
||||
|
||||
#print(dir(self))
|
||||
#print(self.list_properties())
|
||||
self._propstorage = {e.name: e.default_value for e in self.list_properties()}
|
||||
#self.props = {k: e[5] for k, e in } #SnowmixAudioSink.__gproperties__.entries()}
|
||||
|
||||
def do_change_state(self, state):
|
||||
Gst.info('do_change_state(%r)' % (state))
|
||||
|
||||
if state == Gst.StateChange.READY_TO_PAUSED:
|
||||
ip = '' or os.getenv('SNOWMIX_IP') or '127.0.0.1'
|
||||
port = self.props.port or os.getenv('SNOWMIX_PORT') or 9999
|
||||
client = SnowmixClient(ip, port)
|
||||
|
||||
rate = client.audio_rate()[self.props.feed]
|
||||
channels = client.audio_channels()[self.props.feed]
|
||||
|
||||
self.caps.set_property("caps", Gst.Caps.from_string(
|
||||
"audio/x-raw,rate=%d,channels=%d,format=S16LE,layout=interleaved" % (rate, channels)))
|
||||
|
||||
client.close()
|
||||
|
||||
self.socket = Gio.Socket.new(
|
||||
Gio.SocketFamily.IPV4, Gio.SocketType.STREAM,
|
||||
Gio.SocketProtocol.DEFAULT)
|
||||
self.socket.connect(Gio.InetSocketAddress.new_from_string(
|
||||
ip, port))
|
||||
self.socket.send(b'audio feed ctr isaudio %d\n' % (self.props.feed))
|
||||
|
||||
self.sink.emit('add', self.socket)
|
||||
|
||||
return Gst.Bin.do_change_state(self, state)
|
||||
|
||||
def do_set_property(self, prop, value):
|
||||
Gst.info('do_set_property(%r, %r)' % (prop, value))
|
||||
if prop.name not in self._propstorage:
|
||||
raise AttributeError('unknown property %s' % prop.name)
|
||||
self._propstorage[prop.name] = value
|
||||
raise AttributeError('unknown property %s' % prop.name)
|
||||
|
||||
#return Gst.Bin.do_set_property(self, prop, value)
|
||||
|
||||
def do_get_property(self, prop):
|
||||
if prop.name in self._propstorage:
|
||||
return self._propstorage[prop.name]
|
||||
|
||||
def do_render(self, buffer):
|
||||
Gst.info("timestamp(buffer):%s" % (Gst.TIME_ARGS(buffer.pts)))
|
||||
return Gst.FlowReturn.OK
|
||||
|
||||
GObject.type_register(SnowmixAudioSink)
|
||||
__gstelementfactory__ = ("snowmixaudiosink", Gst.Rank.NONE, SnowmixAudioSink)
|
|
@ -0,0 +1 @@
|
|||
../../api/snowmix.py
|
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- Mode: Python -*-
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
|
||||
# sinkelement.py
|
||||
# (c) 2005 Edward Hervey <edward@fluendo.com>
|
||||
# (c) 2007 Jan Schmidt <jan@fluendo.com>
|
||||
# Licensed under LGPL
|
||||
#
|
||||
# Small test application to show how to write a sink element
|
||||
# in 20 lines in python and place into the gstreamer registry
|
||||
# so it can be autoplugged or used from parse_launch.
|
||||
#
|
||||
# You can run the example from the source doing from gst-python/:
|
||||
#
|
||||
# $ export GST_PLUGIN_PATH=$GST_PLUGIN_PATH:$PWD/plugin:$PWD/examples/plugins
|
||||
# $ GST_DEBUG=python:4 gst-launch-1.0 fakesrc num-buffers=10 ! mysink
|
||||
|
||||
|
||||
# inf notes 20190221
|
||||
# https://gist.github.com/jackersson/9d3b0c578c1e625b6b79ea04e2cebd15
|
||||
# https://mathieuduponchelle.github.io/2018-02-01-Python-Elements.html?gi-language=undefined
|
||||
import os
|
||||
from snowmix import SnowmixClient
|
||||
|
||||
from gi.repository import Gst, GObject, GstBase, Gio
|
||||
Gst.init(None)
|
||||
|
||||
#
|
||||
# Simple Sink element created entirely in python
|
||||
#
|
||||
class SnowmixAudioSource(Gst.Bin):
|
||||
__gstmetadata__ = ('CustomSource','Source', \
|
||||
'Custom test source element', 'Edward Hervey')
|
||||
|
||||
__gsttemplates__ = Gst.PadTemplate.new("src",
|
||||
Gst.PadDirection.SRC,
|
||||
Gst.PadPresence.ALWAYS,
|
||||
Gst.Caps.new_any())
|
||||
|
||||
__gproperties__ = {
|
||||
"mixer": (int,
|
||||
"Mixer",
|
||||
"Source mixer number",
|
||||
1,
|
||||
255,
|
||||
1,
|
||||
GObject.ParamFlags.READWRITE
|
||||
),
|
||||
#"host": (str,
|
||||
# "Snowmix Host",
|
||||
# "Target snowmix host",
|
||||
# '',
|
||||
# GObject.ParamFlags.READWRITE
|
||||
# ),
|
||||
"port": (int,
|
||||
"Snowmix Port",
|
||||
"Target snowmix port",
|
||||
0,
|
||||
65535,
|
||||
9999,
|
||||
GObject.ParamFlags.READWRITE
|
||||
),
|
||||
# [...]
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
Gst.info('init(%r, %r)' % (args, kwargs))
|
||||
super(SnowmixAudioSource, self).__init__(*args, **kwargs)
|
||||
self.caps = caps = Gst.ElementFactory.make("capsfilter", None)
|
||||
|
||||
self.source = source = Gst.ElementFactory.make("socketsrc", None)
|
||||
self.add(source, caps)
|
||||
|
||||
# Link from left to right
|
||||
#source.link(caps)
|
||||
#caps.link(sink)
|
||||
|
||||
# Expose first/last
|
||||
self.add_pad(Gst.GhostPad.new("src", source.get_static_pad("src")))
|
||||
|
||||
#print(dir(self))
|
||||
#print(self.list_properties())
|
||||
self._propstorage = {e.name: e.default_value for e in self.list_properties()}
|
||||
#self.props = {k: e[5] for k, e in } #SnowmixAudioSource.__gproperties__.entries()}
|
||||
|
||||
def do_change_state(self, state):
|
||||
Gst.info('do_change_state(%r)' % (state))
|
||||
|
||||
if state == Gst.StateChange.NULL_TO_READY:
|
||||
ip = '' or os.getenv('SNOWMIX_IP') or '127.0.0.1'
|
||||
port = self.props.port or os.getenv('SNOWMIX_PORT') or 9999
|
||||
client = SnowmixClient(ip, port)
|
||||
|
||||
rate = client.audio_rate('sink')[self.props.mixer]
|
||||
channels = client.audio_channels('sink')[self.props.mixer]
|
||||
|
||||
c = Gst.Caps.from_string(
|
||||
"audio/x-raw,rate=%d,channels=%d,format=S16LE,layout=interleaved" % (rate, channels))
|
||||
self.source.set_property('do-timestamp', True)
|
||||
self.source.set_property("caps", c)
|
||||
# self.caps.set_property("caps", c)
|
||||
|
||||
client.close()
|
||||
|
||||
self.socket = Gio.Socket.new(
|
||||
Gio.SocketFamily.IPV4, Gio.SocketType.STREAM,
|
||||
Gio.SocketProtocol.DEFAULT)
|
||||
self.socket.connect(Gio.InetSocketAddress.new_from_string(
|
||||
ip, port))
|
||||
self.socket.send(b'audio sink ctr isaudio %d\n' % (self.props.mixer))
|
||||
|
||||
self.source.set_property('socket', self.socket)
|
||||
|
||||
return Gst.Bin.do_change_state(self, state)
|
||||
|
||||
def do_set_property(self, prop, value):
|
||||
Gst.info('do_set_property(%r, %r)' % (prop, value))
|
||||
if prop.name not in self._propstorage:
|
||||
raise AttributeError('unknown property %s' % prop.name)
|
||||
self._propstorage[prop.name] = value
|
||||
raise AttributeError('unknown property %s' % prop.name)
|
||||
|
||||
#return Gst.Bin.do_set_property(self, prop, value)
|
||||
|
||||
def do_get_property(self, prop):
|
||||
if prop.name in self._propstorage:
|
||||
return self._propstorage[prop.name]
|
||||
|
||||
def do_render(self, buffer):
|
||||
Gst.info("timestamp(buffer):%s" % (Gst.TIME_ARGS(buffer.pts)))
|
||||
return Gst.FlowReturn.OK
|
||||
|
||||
GObject.type_register(SnowmixAudioSource)
|
||||
__gstelementfactory__ = ("snowmixaudiosrc", Gst.Rank.NONE, SnowmixAudioSource)
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
export GST_PLUGIN_PATH=$GST_PLUGIN_PATH:~/Projects/gst-python/plugin/:$PWD
|
||||
export GST_DEBUG=python:4
|
||||
|
||||
gst-inspect-1.0 | grep -i py
|
||||
echo ====
|
||||
gst-launch-1.0 -v pulsesrc server=sound.waw.hackerspace.pl buffer-time=2000000 do-timestamp=true ! audioconvert ! audioresample ! snowmixaudiosink feed=2
|
||||
#gst-launch-1.0 -v pulsesrc server=sound.waw.hackerspace.pl ! snowmixaudiosink feed=2
|
||||
# feed=4
|
||||
#GST_DEBUG=python:4 gst-launch-1.0 fakesrc num-buffers=10 ! identity_py ! fakesink
|
||||
#gst-launch-1.0 -v audiotestsrc_py is-live=true ! fakesink silent=false
|
Loading…
Reference in New Issue