aboutsummaryrefslogtreecommitdiff
path: root/voctocore/lib/shmsrc.py
blob: 0224386428abb824c46964a265985b113ca876ef (plain)
  1. #!/usr/bin/python3
  2. import time, logging
  3. from gi.repository import GLib, Gst
  4. from lib.config import Config
  5. class FailsafeShmSrc(Gst.Bin):
  6. log = logging.getLogger('FailsafeShmSrc')
  7. last_buffer_arrived = 0
  8. is_in_failstate = True
  9. def __init__(self, socket):
  10. super().__init__()
  11. caps = Gst.Caps.from_string(Config.get('sources', 'videocaps'))
  12. self.log.debug('parsing videocaps from config: %s', caps.to_string())
  13. # Create elements
  14. self.shmsrc = Gst.ElementFactory.make('shmsrc', None)
  15. self.identity1 = Gst.ElementFactory.make('identity', None)
  16. self.identity2 = Gst.ElementFactory.make('identity', None)
  17. self.switch = Gst.ElementFactory.make('input-selector', None)
  18. self.failsrc = Gst.ElementFactory.make('videotestsrc', None)
  19. if not self.shmsrc or not self.identity1 or not self.identity2 or not self.switch or not self.failsrc:
  20. self.log.error('could not create elements')
  21. # Add elements to Bin
  22. self.add(self.shmsrc)
  23. self.add(self.identity1)
  24. self.add(self.identity2)
  25. self.add(self.switch)
  26. self.add(self.failsrc)
  27. # Get Switcher-Pads
  28. self.goodpad = self.switch.get_request_pad('sink_%u')
  29. self.failpad = self.switch.get_request_pad('sink_%u')
  30. # Set properties
  31. self.shmsrc.set_property('socket-path', socket)
  32. self.shmsrc.set_property('is-live', True)
  33. self.shmsrc.set_property('do-timestamp', True)
  34. self.identity2.set_property('sync', True)
  35. self.switch.set_property('active-pad', self.failpad)
  36. # Link elements
  37. self.shmsrc.link_filtered(self.identity1, caps)
  38. self.identity1.get_static_pad('src').link(self.goodpad)
  39. self.failsrc.link_filtered(self.identity2, caps)
  40. self.identity2.get_static_pad('src').link(self.failpad)
  41. # Install pad probes
  42. self.shmsrc.get_static_pad('src').add_probe(Gst.PadProbeType.BLOCK | Gst.PadProbeType.EVENT_DOWNSTREAM, self.event_probe, None)
  43. self.shmsrc.get_static_pad('src').add_probe(Gst.PadProbeType.BLOCK | Gst.PadProbeType.BUFFER, self.data_probe, None)
  44. # Install Watchdog
  45. GLib.timeout_add(500, self.watchdog)
  46. # Add Ghost Pads
  47. self.add_pad(
  48. Gst.GhostPad.new('src', self.switch.get_static_pad('src'))
  49. )
  50. def do_handle_message(self, msg):
  51. if msg.type == Gst.MessageType.ERROR and msg.src == self.shmsrc:
  52. (err, debug) = msg.parse_error()
  53. self.log.warning('received error-message from ShmSrc, dropping: %s', err)
  54. self.log.debug(' debug-info from shmsrc: %s', debug)
  55. else:
  56. Gst.Bin.do_handle_message(self, msg)
  57. def event_probe(self, pad, info, ud):
  58. e = info.get_event()
  59. if e.type == Gst.EventType.EOS:
  60. self.log.warning('received EOS-event on event-probe, dropping')
  61. self.switch_to_failstate()
  62. return Gst.PadProbeReturn.DROP
  63. return Gst.PadProbeReturn.PASS
  64. def data_probe(self, pad, info, ud):
  65. self.last_buffer_arrived = time.time()
  66. self.switch_to_goodstate()
  67. return Gst.PadProbeReturn.PASS
  68. def watchdog(self):
  69. if self.last_buffer_arrived + 0.1 < time.time():
  70. self.log.warning('watchdog encountered a timeout')
  71. self.switch_to_failstate()
  72. if self.last_buffer_arrived + 3 < time.time() and round(time.time() % 3) == 0:
  73. self.restart()
  74. return True
  75. def restart(self):
  76. self.log.warning('restarting ShmSrc')
  77. self.shmsrc.set_state(Gst.State.NULL)
  78. self.shmsrc.set_base_time(self.get_parent().get_base_time())
  79. self.shmsrc.set_state(Gst.State.PLAYING)
  80. def switch_to_goodstate(self):
  81. if not self.is_in_failstate:
  82. return
  83. self.log.warning('switching output to goodstate')
  84. self.is_in_failstate = False
  85. self.switch.set_property('active-pad', self.goodpad)
  86. def switch_to_failstate(self):
  87. if self.is_in_failstate:
  88. return
  89. self.log.warning('switching output to failstate')
  90. self.is_in_failstate = True
  91. self.switch.set_property('active-pad', self.failpad)