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