From 9bfd642a86702ee6b597b1f532aeac8ccf0fe05b Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Sat, 5 Mar 2016 23:52:57 -0600 Subject: multi device client with netclock and monitor. --- clients/source/ingest.py | 293 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100755 clients/source/ingest.py diff --git a/clients/source/ingest.py b/clients/source/ingest.py new file mode 100755 index 0000000..2ac2b7f --- /dev/null +++ b/clients/source/ingest.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python3 +""" +Ryan Verner +Voctomix ingest streams used for LCA2016, hacked up on the fly. + +PIPELINES: + * dv + * dvpulse + * hdvpulse + * hdmi2usb + * blackmagichdmi + * test + +Example intended uses (NOTE expected environment variables): + * lca-videomix-ingest.py dvpulse 0 + * lca-videomix-ingest.py hdmi2usb 1 +""" + +import sys + +import gi +import signal +import os +import socket + +import argparse + +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([]) + +sys.path.insert(0, '../..' ) + +import voctogui.lib.connection as Connection +# import lib.clock as ClockManager + + +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 do-timestamp=true name=videosrc {video_device} ! + tsdemux name=demux! + queue ! + decodebin ! + {monitor} + deinterlace mode=1 ! + videorate ! + videoscale ! + videoconvert ! + """ + + elif args.video_source == 'hdmi2usb': + video_src = """ + v4l2src device=%s name=videosrc ! + queue ! + image/jpeg,width=1280,height=720 ! + jpegdec ! + {monitor} + videoconvert ! + videorate ! + """ + + 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 == '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 + + 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 + + # obtain network-clock + ClockManager.obtainClock(Connection.ip) + + +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') + + + + +def get_args(): + + parser = argparse.ArgumentParser(description='Vocto-ingesb') + + 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', '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() -- cgit v1.2.3 From e10725d1792c1a519b49a2c2130780c5c6b6bf67 Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Sun, 6 Mar 2016 00:11:00 -0600 Subject: remove outdated docs --- clients/source/ingest.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/clients/source/ingest.py b/clients/source/ingest.py index 2ac2b7f..12e9cc4 100755 --- a/clients/source/ingest.py +++ b/clients/source/ingest.py @@ -1,20 +1,7 @@ #!/usr/bin/env python3 -""" -Ryan Verner -Voctomix ingest streams used for LCA2016, hacked up on the fly. - -PIPELINES: - * dv - * dvpulse - * hdvpulse - * hdmi2usb - * blackmagichdmi - * test - -Example intended uses (NOTE expected environment variables): - * lca-videomix-ingest.py dvpulse 0 - * lca-videomix-ingest.py hdmi2usb 1 -""" + +# ingest.py +# source client for Voctomix import sys -- cgit v1.2.3 From 7148946afa2b91cf93e4fb792f3c7925e4497219 Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Wed, 23 Mar 2016 01:24:04 -0500 Subject: added --video-source ximage for screen capture. --- clients/source/ingest.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/clients/source/ingest.py b/clients/source/ingest.py index 12e9cc4..93b9a84 100755 --- a/clients/source/ingest.py +++ b/clients/source/ingest.py @@ -19,6 +19,7 @@ from gi.repository import Gst, GstNet, GObject GObject.threads_init() Gst.init([]) +# this is kinda icky. sys.path.insert(0, '../..' ) import voctogui.lib.connection as Connection @@ -74,6 +75,14 @@ def mk_video_src(args, videocaps): videorate ! """ + elif args.video_source == 'ximage': + video_src = """ + ximagesrc name=videosrc ! + videoconvert ! + videorate ! + videoscale ! + """ + elif args.video_source == 'blackmagichdmi': video_src = """ decklinkvideosrc mode=17 connection=2 ! @@ -154,7 +163,10 @@ def mk_pipeline(args, server_caps): mux = mk_mux(args) client = mk_client(args) - pipeline = video_src + "mux. \n" + audio_src + mux + client + 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 @@ -217,13 +229,16 @@ def run_pipeline(pipeline, args): def get_args(): - parser = argparse.ArgumentParser(description='Vocto-ingesb') + parser = argparse.ArgumentParser(description='Vocto-ingest client') 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', 'test'], + choices=[ + 'dv', 'hdv', 'hdmi2usb', 'blackmagichdmi', + 'ximage', + 'test', ], default='test', help="Where to get video from") -- cgit v1.2.3 From 88e9eaaef470963996ecdbd839aededf35040940 Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Wed, 23 Mar 2016 10:00:01 -0500 Subject: add --monitor to ximage for debugging. --- clients/source/ingest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/source/ingest.py b/clients/source/ingest.py index 93b9a84..1e8fd40 100755 --- a/clients/source/ingest.py +++ b/clients/source/ingest.py @@ -78,6 +78,7 @@ def mk_video_src(args, videocaps): elif args.video_source == 'ximage': video_src = """ ximagesrc name=videosrc ! + {monitor} videoconvert ! videorate ! videoscale ! -- cgit v1.2.3 From 0657739a8ac19a57b8a72cb485f4574378a63f75 Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Wed, 23 Mar 2016 20:46:22 -0500 Subject: add use-damage=false so all frames are sent, not just on change. --- clients/source/ingest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clients/source/ingest.py b/clients/source/ingest.py index 1e8fd40..2b4c425 100755 --- a/clients/source/ingest.py +++ b/clients/source/ingest.py @@ -77,12 +77,14 @@ def mk_video_src(args, videocaps): elif args.video_source == 'ximage': video_src = """ - ximagesrc name=videosrc ! + 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 = """ -- cgit v1.2.3 From a420fd2adea0e5a0d3b8ff2034db036cb76e19bc Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Tue, 12 Apr 2016 16:58:37 -0500 Subject: minor cleanup --- clients/source/ingest.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clients/source/ingest.py b/clients/source/ingest.py index 2b4c425..446f1a6 100755 --- a/clients/source/ingest.py +++ b/clients/source/ingest.py @@ -21,6 +21,7 @@ Gst.init([]) # this is kinda icky. sys.path.insert(0, '../..' ) +sys.path.insert(0, '.' ) import voctogui.lib.connection as Connection # import lib.clock as ClockManager @@ -53,7 +54,7 @@ def mk_video_src(args, videocaps): elif args.video_source == 'hdv': video_src = """ - hdv1394src do-timestamp=true name=videosrc {video_device} ! + hdv1394src {video_device} do-timestamp=true name=videosrc ! tsdemux name=demux! queue ! decodebin ! @@ -66,7 +67,7 @@ def mk_video_src(args, videocaps): elif args.video_source == 'hdmi2usb': video_src = """ - v4l2src device=%s name=videosrc ! + v4l2src {video_device} name=videosrc ! queue ! image/jpeg,width=1280,height=720 ! jpegdec ! -- cgit v1.2.3 From a7d75572332c0013c253c7a05e227a70b0c6abd7 Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Sat, 16 Apr 2016 17:10:42 -0500 Subject: Add generic hlp text for beginners. --- clients/source/ingest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clients/source/ingest.py b/clients/source/ingest.py index 446f1a6..dbd4a4b 100755 --- a/clients/source/ingest.py +++ b/clients/source/ingest.py @@ -233,7 +233,11 @@ def run_pipeline(pipeline, args): def get_args(): - parser = argparse.ArgumentParser(description='Vocto-ingest client') + 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.") -- cgit v1.2.3 From 302e9d304e0e59c17b201e8e7d78f913433eaa6a Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Sat, 16 Apr 2016 17:14:58 -0500 Subject: Cleanly shutdown pipeline on ^c --- clients/source/ingest.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clients/source/ingest.py b/clients/source/ingest.py index dbd4a4b..2b63dde 100755 --- a/clients/source/ingest.py +++ b/clients/source/ingest.py @@ -228,8 +228,11 @@ def run_pipeline(pipeline, args): except KeyboardInterrupt: print('Terminated via Ctrl-C') + print('Shutting down...') + senderPipeline.set_state(Gst.State.NULL) + print('Done.') - + return def get_args(): -- cgit v1.2.3 From abad5efe4e1665bc6e49fb69684db1fc183dee5e Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Tue, 19 Apr 2016 16:48:24 -0500 Subject: Added some #comments. --- clients/source/ingest.py | 306 ----------------------------------- example-scripts/gstreamer/ingest.py | 310 ++++++++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+), 306 deletions(-) delete mode 100755 clients/source/ingest.py create mode 100755 example-scripts/gstreamer/ingest.py diff --git a/clients/source/ingest.py b/clients/source/ingest.py deleted file mode 100755 index 2b63dde..0000000 --- a/clients/source/ingest.py +++ /dev/null @@ -1,306 +0,0 @@ -#!/usr/bin/env python3 - -# ingest.py -# source client for Voctomix - -import sys - -import gi -import signal -import os -import socket - -import argparse - -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 kinda icky. -sys.path.insert(0, '../..' ) -sys.path.insert(0, '.' ) - -import voctogui.lib.connection as Connection -# import lib.clock as ClockManager - - -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': - 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 == '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 - - # obtain network-clock - ClockManager.obtainClock(Connection.ip) - - -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() diff --git a/example-scripts/gstreamer/ingest.py b/example-scripts/gstreamer/ingest.py new file mode 100755 index 0000000..14ce043 --- /dev/null +++ b/example-scripts/gstreamer/ingest.py @@ -0,0 +1,310 @@ +#!/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 == '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() -- cgit v1.2.3 From 79efc5f2fccc377933ff6863c2bba21cdf506446 Mon Sep 17 00:00:00 2001 From: Carl Karsten Date: Wed, 27 Apr 2016 09:56:55 -0500 Subject: add support for alsa --- example-scripts/gstreamer/ingest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/example-scripts/gstreamer/ingest.py b/example-scripts/gstreamer/ingest.py index 14ce043..a9e06c4 100755 --- a/example-scripts/gstreamer/ingest.py +++ b/example-scripts/gstreamer/ingest.py @@ -138,6 +138,11 @@ def mk_audio_src(args, audiocaps): 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 ! -- cgit v1.2.3