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