aboutsummaryrefslogtreecommitdiff
path: root/clients/source/ingest.py
blob: 12e9cc45f18574f2ffa0f229184f0902f02ebdb2 (plain)
  1. #!/usr/bin/env python3
  2. # ingest.py
  3. # source client for Voctomix
  4. import sys
  5. import gi
  6. import signal
  7. import os
  8. import socket
  9. import argparse
  10. gi.require_version('Gst', '1.0')
  11. from gi.repository import Gst, GstNet, GObject
  12. # init GObject & Co. before importing local classes
  13. GObject.threads_init()
  14. Gst.init([])
  15. sys.path.insert(0, '../..' )
  16. import voctogui.lib.connection as Connection
  17. # import lib.clock as ClockManager
  18. def mk_video_src(args, videocaps):
  19. # make video soure part of pipeline
  20. video_device = "device={}".format(args.video_dev) \
  21. if args.video_dev else ""
  22. monitor = """tee name=t ! queue !
  23. videoconvert ! fpsdisplaysink sync=false
  24. t. ! queue !""" \
  25. if args.monitor else ""
  26. if args.video_source == 'dv':
  27. video_src = """
  28. dv1394src name=videosrc {video_device}!
  29. dvdemux name=demux !
  30. queue !
  31. dvdec !
  32. {monitor}
  33. deinterlace mode=1 !
  34. videoconvert !
  35. videorate !
  36. videoscale !
  37. """
  38. elif args.video_source == 'hdv':
  39. video_src = """
  40. hdv1394src do-timestamp=true name=videosrc {video_device} !
  41. tsdemux name=demux!
  42. queue !
  43. decodebin !
  44. {monitor}
  45. deinterlace mode=1 !
  46. videorate !
  47. videoscale !
  48. videoconvert !
  49. """
  50. elif args.video_source == 'hdmi2usb':
  51. video_src = """
  52. v4l2src device=%s name=videosrc !
  53. queue !
  54. image/jpeg,width=1280,height=720 !
  55. jpegdec !
  56. {monitor}
  57. videoconvert !
  58. videorate !
  59. """
  60. elif args.video_source == 'blackmagichdmi':
  61. video_src = """
  62. decklinkvideosrc mode=17 connection=2 !
  63. {monitor}
  64. videoconvert !
  65. videorate !
  66. videoscale !
  67. """
  68. elif args.video_source == 'test':
  69. video_src = """
  70. videotestsrc name=videosrc
  71. pattern=ball
  72. foreground-color=0x00ff0000 background-color=0x00440000 !
  73. {monitor}
  74. """
  75. video_src = video_src.format(
  76. video_device=video_device,
  77. monitor=monitor)
  78. video_src += videocaps + "!\n"
  79. return video_src
  80. def mk_audio_src(args, audiocaps):
  81. audio_device = "device={}".format(args.audio_dev) \
  82. if args.audio_dev else ""
  83. if args.audio_source in [ 'dv', 'hdv' ]:
  84. # this only works if video is from DV also.
  85. # or some gst source that gets demux ed
  86. audio_src = """
  87. demux. !
  88. audioconvert !
  89. """
  90. elif args.audio_source == 'pulse':
  91. audio_src = """
  92. pulsesrc {audio_device} name=audiosrc !
  93. """.format(audio_device=audio_device)
  94. elif args.audio_source == 'blackmagichdmi':
  95. audio_src = """
  96. decklinkaudiosrc !
  97. """
  98. elif args.audio_source == 'test':
  99. audio_src = """
  100. audiotestsrc name=audiosrc freq=330 !
  101. """
  102. audio_src += audiocaps + "!\n"
  103. return audio_src
  104. def mk_mux(args):
  105. mux = """
  106. mux.
  107. matroskamux name=mux !
  108. """
  109. return mux
  110. def mk_client(args):
  111. core_ip = socket.gethostbyname(args.host)
  112. client = """
  113. tcpclientsink host={host} port={port}
  114. """.format(host=core_ip, port=args.port)
  115. return client
  116. def mk_pipeline(args, server_caps):
  117. video_src = mk_video_src(args, server_caps['videocaps'])
  118. audio_src = mk_audio_src(args, server_caps['audiocaps'])
  119. mux = mk_mux(args)
  120. client = mk_client(args)
  121. pipeline = video_src + "mux. \n" + audio_src + mux + client
  122. return pipeline
  123. def get_server_caps():
  124. # fetch config from server
  125. server_config = Connection.fetchServerConfig()
  126. server_caps = {'videocaps': server_config['mix']['videocaps'],
  127. 'audiocaps': server_config['mix']['audiocaps']}
  128. return server_caps
  129. # obtain network-clock
  130. ClockManager.obtainClock(Connection.ip)
  131. def run_pipeline(pipeline, args):
  132. core_ip = socket.gethostbyname(args.host)
  133. clock = GstNet.NetClientClock.new('voctocore', core_ip, 9998, 0)
  134. print('obtained NetClientClock from host', clock)
  135. print('waiting for NetClientClock to sync…')
  136. clock.wait_for_sync(Gst.CLOCK_TIME_NONE)
  137. print('starting pipeline')
  138. senderPipeline = Gst.parse_launch(pipeline)
  139. senderPipeline.use_clock(clock)
  140. src = senderPipeline.get_by_name('src')
  141. def on_eos(self, bus, message):
  142. print('Received EOS-Signal')
  143. sys.exit(1)
  144. def on_error(self, bus, message):
  145. print('Received Error-Signal')
  146. (error, debug) = message.parse_error()
  147. print('Error-Details: #%u: %s' % (error.code, debug))
  148. sys.exit(1)
  149. # Binding End-of-Stream-Signal on Source-Pipeline
  150. senderPipeline.bus.add_signal_watch()
  151. senderPipeline.bus.connect("message::eos", on_eos)
  152. senderPipeline.bus.connect("message::error", on_error)
  153. print("playing")
  154. senderPipeline.set_state(Gst.State.PLAYING)
  155. mainloop = GObject.MainLoop()
  156. try:
  157. mainloop.run()
  158. except KeyboardInterrupt:
  159. print('Terminated via Ctrl-C')
  160. def get_args():
  161. parser = argparse.ArgumentParser(description='Vocto-ingesb')
  162. parser.add_argument('-v', '--verbose', action='count', default=0,
  163. help="Also print INFO and DEBUG messages.")
  164. parser.add_argument( '--video-source', action='store',
  165. choices=['dv', 'hdv', 'hdmi2usb', 'blackmagichdmi', 'test'],
  166. default='test',
  167. help="Where to get video from")
  168. parser.add_argument( '--video-dev', action='store',
  169. help="video device")
  170. parser.add_argument( '--audio-source', action='store',
  171. choices=['dv', 'alsa', 'pulse', 'blackmagichdmi', 'test'],
  172. default='test',
  173. help="Where to get audio from")
  174. parser.add_argument( '--audio-dev', action='store',
  175. default='hw:CARD=CODEC',
  176. help="for alsa/pulse, audio device")
  177. # maybe hw:1,0
  178. parser.add_argument( '--audio-delay', action='store',
  179. default='10',
  180. help="ms to delay audio")
  181. parser.add_argument('-m', '--monitor', action='store_true',
  182. help="fps display sink")
  183. parser.add_argument( '--host', action='store',
  184. default='localhost',
  185. help="hostname of vocto core")
  186. parser.add_argument( '--port', action='store',
  187. default='10000',
  188. help="port of vocto core")
  189. args = parser.parse_args()
  190. return args
  191. def main():
  192. args = get_args()
  193. core_ip = socket.gethostbyname(args.host)
  194. # establish a synchronus connection to server
  195. Connection.establish(core_ip)
  196. server_caps = get_server_caps()
  197. pipeline = mk_pipeline(args, server_caps)
  198. print(pipeline)
  199. run_pipeline(pipeline, args)
  200. if __name__ == '__main__':
  201. main()