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