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