aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaZderMind <git@mazdermind.de>2016-04-27 23:15:26 +0200
committerMaZderMind <git@mazdermind.de>2016-04-27 23:15:26 +0200
commit0fed1b4e54126f429560aaeb1bcf2439827f388d (patch)
treef563b4377bde45e7df3ba32b6ebe2b95e281f815
parent501f129e694ad076460c629cade52e757ffd844f (diff)
parent79efc5f2fccc377933ff6863c2bba21cdf506446 (diff)
Merge branch 'CarlFK/ingest-client'0.3
-rwxr-xr-xexample-scripts/gstreamer/ingest.py315
1 files changed, 315 insertions, 0 deletions
diff --git a/example-scripts/gstreamer/ingest.py b/example-scripts/gstreamer/ingest.py
new file mode 100755
index 0000000..a9e06c4
--- /dev/null
+++ b/example-scripts/gstreamer/ingest.py
@@ -0,0 +1,315 @@
+#!/usr/bin/env python3
+
+# ingest.py
+"""
+Source client for Voctomix.
+Features:
+ Retrieves audio and video-caps config from core.
+ Uses core's clock.
+ Mix and match audio and video sources muxed into one streem.
+ Can display video locally, including frame count and fps.
+ Defaults to test audio and video sent to local core.
+
+"""
+
+import argparse
+import gi
+import os
+import signal
+import socket
+import sys
+
+gi.require_version('Gst', '1.0')
+from gi.repository import Gst, GstNet, GObject
+
+# init GObject & Co. before importing local classes
+GObject.threads_init()
+Gst.init([])
+
+# this is to use the same code tha gui uses to get config from core
+sys.path.insert(0, '../..' )
+sys.path.insert(0, '.' )
+
+import voctogui.lib.connection as Connection
+
+
+def mk_video_src(args, videocaps):
+ # make video soure part of pipeline
+
+ video_device = "device={}".format(args.video_dev) \
+ if args.video_dev else ""
+
+ monitor = """tee name=t ! queue !
+ videoconvert ! fpsdisplaysink sync=false
+ t. ! queue !""" \
+ if args.monitor else ""
+
+
+ if args.video_source == 'dv':
+ video_src = """
+ dv1394src name=videosrc {video_device}!
+ dvdemux name=demux !
+ queue !
+ dvdec !
+ {monitor}
+ deinterlace mode=1 !
+ videoconvert !
+ videorate !
+ videoscale !
+ """
+
+ elif args.video_source == 'hdv':
+ video_src = """
+ hdv1394src {video_device} do-timestamp=true name=videosrc !
+ tsdemux name=demux!
+ queue !
+ decodebin !
+ {monitor}
+ deinterlace mode=1 !
+ videorate !
+ videoscale !
+ videoconvert !
+ """
+
+ elif args.video_source == 'hdmi2usb':
+ # https://hdmi2usb.tv
+ # Note: this code works with 720p
+ video_src = """
+ v4l2src {video_device} name=videosrc !
+ queue !
+ image/jpeg,width=1280,height=720 !
+ jpegdec !
+ {monitor}
+ videoconvert !
+ videorate !
+ """
+
+ elif args.video_source == 'ximage':
+ video_src = """
+ ximagesrc name=videosrc
+ use-damage=false !
+ {monitor}
+ videoconvert !
+ videorate !
+ videoscale !
+ """
+ # startx=0 starty=0 endx=1919 endy=1079 !
+
+ elif args.video_source == 'blackmagichdmi':
+ video_src = """
+ decklinkvideosrc mode=17 connection=2 !
+ {monitor}
+ videoconvert !
+ videorate !
+ videoscale !
+ """
+
+ elif args.video_source == 'test':
+ video_src = """
+ videotestsrc name=videosrc
+ pattern=ball
+ foreground-color=0x00ff0000 background-color=0x00440000 !
+ {monitor}
+ """
+
+ video_src = video_src.format(
+ video_device=video_device,
+ monitor=monitor)
+
+ video_src += videocaps + "!\n"
+
+ return video_src
+
+def mk_audio_src(args, audiocaps):
+
+ audio_device = "device={}".format(args.audio_dev) \
+ if args.audio_dev else ""
+
+ if args.audio_source in [ 'dv', 'hdv' ]:
+ # this only works if video is from DV also.
+ # or some gst source that gets demux ed
+ audio_src = """
+ demux. !
+ audioconvert !
+ """
+
+ elif args.audio_source == 'pulse':
+ audio_src = """
+ pulsesrc {audio_device} name=audiosrc !
+ """.format(audio_device=audio_device)
+
+ elif args.audio_source == 'alsa':
+ audio_src = """
+ alsasrc {audio_device} name=audiosrc !
+ """.format(audio_device=audio_device)
+
+ elif args.audio_source == 'blackmagichdmi':
+ audio_src = """
+ decklinkaudiosrc !
+ """
+
+ elif args.audio_source == 'test':
+ audio_src = """
+ audiotestsrc name=audiosrc freq=330 !
+ """
+ audio_src += audiocaps + "!\n"
+
+ return audio_src
+
+def mk_mux(args):
+
+ mux = """
+ mux.
+ matroskamux name=mux !
+ """
+ return mux
+
+def mk_client(args):
+ core_ip = socket.gethostbyname(args.host)
+ client = """
+ tcpclientsink host={host} port={port}
+ """.format(host=core_ip, port=args.port)
+
+ return client
+
+
+def mk_pipeline(args, server_caps):
+
+ video_src = mk_video_src(args, server_caps['videocaps'])
+ audio_src = mk_audio_src(args, server_caps['audiocaps'])
+ mux = mk_mux(args)
+ client = mk_client(args)
+
+ pipeline = video_src + "mux.\n" + audio_src + mux + client
+
+ # remove blank lines to make it more human readable
+ pipeline = pipeline.replace("\n\n","\n")
+
+ return pipeline
+
+def get_server_caps():
+
+
+ # fetch config from server
+ server_config = Connection.fetchServerConfig()
+ server_caps = {'videocaps': server_config['mix']['videocaps'],
+ 'audiocaps': server_config['mix']['audiocaps']}
+
+ return server_caps
+
+def run_pipeline(pipeline, args):
+
+ core_ip = socket.gethostbyname(args.host)
+
+ clock = GstNet.NetClientClock.new('voctocore', core_ip, 9998, 0)
+ print('obtained NetClientClock from host', clock)
+
+ print('waiting for NetClientClock to sync…')
+ clock.wait_for_sync(Gst.CLOCK_TIME_NONE)
+
+ print('starting pipeline')
+ senderPipeline = Gst.parse_launch(pipeline)
+ senderPipeline.use_clock(clock)
+ src = senderPipeline.get_by_name('src')
+
+ def on_eos(self, bus, message):
+ print('Received EOS-Signal')
+ sys.exit(1)
+
+ def on_error(self, bus, message):
+ print('Received Error-Signal')
+ (error, debug) = message.parse_error()
+ print('Error-Details: #%u: %s' % (error.code, debug))
+ sys.exit(1)
+
+
+ # Binding End-of-Stream-Signal on Source-Pipeline
+ senderPipeline.bus.add_signal_watch()
+ senderPipeline.bus.connect("message::eos", on_eos)
+ senderPipeline.bus.connect("message::error", on_error)
+
+ print("playing")
+ senderPipeline.set_state(Gst.State.PLAYING)
+
+ mainloop = GObject.MainLoop()
+ try:
+ mainloop.run()
+ except KeyboardInterrupt:
+ print('Terminated via Ctrl-C')
+
+ print('Shutting down...')
+ senderPipeline.set_state(Gst.State.NULL)
+ print('Done.')
+
+ return
+
+def get_args():
+
+ parser = argparse.ArgumentParser(
+ description='''Vocto-ingest Client with Net-time support.
+ Gst caps are retrieved from the server.
+ Run without parameters: send test av to localhost:10000
+ ''')
+
+ parser.add_argument('-v', '--verbose', action='count', default=0,
+ help="Also print INFO and DEBUG messages.")
+
+ parser.add_argument( '--video-source', action='store',
+ choices=[
+ 'dv', 'hdv', 'hdmi2usb', 'blackmagichdmi',
+ 'ximage',
+ 'test', ],
+ default='test',
+ help="Where to get video from")
+
+ parser.add_argument( '--video-dev', action='store',
+ help="video device")
+
+ parser.add_argument( '--audio-source', action='store',
+ choices=['dv', 'alsa', 'pulse', 'blackmagichdmi', 'test'],
+ default='test',
+ help="Where to get audio from")
+
+ parser.add_argument( '--audio-dev', action='store',
+ default='hw:CARD=CODEC',
+ help="for alsa/pulse, audio device")
+ # maybe hw:1,0
+
+ parser.add_argument( '--audio-delay', action='store',
+ default='10',
+ help="ms to delay audio")
+
+ parser.add_argument('-m', '--monitor', action='store_true',
+ help="fps display sink")
+
+ parser.add_argument( '--host', action='store',
+ default='localhost',
+ help="hostname of vocto core")
+
+ parser.add_argument( '--port', action='store',
+ default='10000',
+ help="port of vocto core")
+
+ args = parser.parse_args()
+
+ return args
+
+
+def main():
+
+ args = get_args()
+
+ core_ip = socket.gethostbyname(args.host)
+ # establish a synchronus connection to server
+ Connection.establish(core_ip)
+
+ server_caps = get_server_caps()
+
+ pipeline = mk_pipeline(args, server_caps)
+ print(pipeline)
+ run_pipeline(pipeline, args)
+
+
+
+if __name__ == '__main__':
+ main()