aboutsummaryrefslogtreecommitdiff
path: root/voctogui/lib/videodisplay.py
blob: 744e1aa06fc8fdbc235dadf428a1dda2383bc7c0 (plain)
  1. import logging
  2. from gi.repository import Gst
  3. from lib.args import Args
  4. from lib.config import Config
  5. from lib.clock import Clock
  6. class VideoDisplay(object):
  7. """ Displays a Voctomix-Video-Stream into a GtkWidget """
  8. def __init__(self, drawing_area, port, width=None, height=None, play_audio=False, level_callback=None):
  9. self.log = logging.getLogger('VideoDisplay[%u]' % port)
  10. self.drawing_area = drawing_area
  11. self.level_callback = level_callback
  12. caps = Config.get('mix', 'videocaps')
  13. use_previews = Config.getboolean('previews', 'enabled') and Config.getboolean('previews', 'use')
  14. # Preview-Ports are Raw-Ports + 1000
  15. if use_previews:
  16. self.log.info('using jpeg-previews instead of raw-video for gui')
  17. port += 1000
  18. else:
  19. self.log.info('using raw-video instead of jpeg-previews for gui')
  20. # Setup Server-Connection, Demuxing and Decoding
  21. pipeline = """
  22. tcpclientsrc host={host} port={port} blocksize=1048576 !
  23. queue !
  24. matroskademux name=demux
  25. """
  26. if use_previews:
  27. pipeline += """
  28. demux. !
  29. image/jpeg !
  30. jpegdec !
  31. {previewcaps} !
  32. queue !
  33. """
  34. else:
  35. pipeline += """
  36. demux. !
  37. {vcaps} !
  38. queue !
  39. """
  40. # Video Display
  41. videosystem = Config.get('videodisplay', 'system')
  42. self.log.debug('Configuring for Video-System %s', videosystem)
  43. if videosystem == 'gl':
  44. pipeline += """
  45. glupload !
  46. glcolorconvert !
  47. glimagesinkelement
  48. """
  49. elif videosystem == 'xv':
  50. pipeline += """
  51. xvimagesink
  52. """
  53. elif videosystem == 'x':
  54. prescale_caps = 'video/x-raw'
  55. if width and height:
  56. prescale_caps += ',width=%u,height=%u' % (width, height)
  57. pipeline += """
  58. videoconvert !
  59. videoscale !
  60. {prescale_caps} !
  61. ximagesink
  62. """.format(
  63. prescale_caps=prescale_caps
  64. )
  65. else:
  66. raise Exception('Invalid Videodisplay-System configured: %s' % videosystem)
  67. # If an Audio-Path is required, add an Audio-Path through a level-Element
  68. if self.level_callback or play_audio:
  69. pipeline += """
  70. demux. !
  71. {acaps} !
  72. queue !
  73. level name=lvl interval=50000000 !
  74. """
  75. # If Playback is requested, push fo pulseaudio
  76. if play_audio:
  77. pipeline += """
  78. pulsesink
  79. """
  80. # Otherwise just trash the Audio
  81. else:
  82. pipeline += """
  83. fakesink
  84. """
  85. pipeline = pipeline.format(
  86. acaps=Config.get('mix', 'audiocaps'),
  87. vcaps=Config.get('mix', 'videocaps'),
  88. previewcaps=Config.get('previews', 'videocaps'),
  89. host=Args.host if Args.host else Config.get('server', 'host'),
  90. port=port,
  91. )
  92. self.log.debug('Creating Display-Pipeline:\n%s', pipeline)
  93. self.pipeline = Gst.parse_launch(pipeline)
  94. self.pipeline.use_clock(Clock)
  95. self.drawing_area.realize()
  96. self.xid = self.drawing_area.get_property('window').get_xid()
  97. self.log.debug('Realized Drawing-Area with xid %u', self.xid)
  98. bus = self.pipeline.get_bus()
  99. bus.add_signal_watch()
  100. bus.enable_sync_message_emission()
  101. bus.connect('message::error', self.on_error)
  102. bus.connect("sync-message::element", self.on_syncmsg)
  103. if self.level_callback:
  104. bus.connect("message::element", self.on_level)
  105. self.log.debug('Launching Display-Pipeline')
  106. self.pipeline.set_state(Gst.State.PLAYING)
  107. def on_syncmsg(self, bus, msg):
  108. if msg.get_structure().get_name() == "prepare-window-handle":
  109. self.log.info('Setting imagesink window-handle to %s', self.xid)
  110. msg.src.set_window_handle(self.xid)
  111. def on_error(self, bus, message):
  112. self.log.debug('Received Error-Signal on Display-Pipeline')
  113. (error, debug) = message.parse_error()
  114. self.log.debug('Error-Details: #%u: %s', error.code, debug)
  115. def on_level(self, bus, msg):
  116. if msg.src.name != 'lvl':
  117. return
  118. if msg.type != Gst.MessageType.ELEMENT:
  119. return
  120. rms = msg.get_structure().get_value('rms')
  121. peak = msg.get_structure().get_value('peak')
  122. decay = msg.get_structure().get_value('decay')
  123. self.level_callback(rms, peak, decay)