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