aboutsummaryrefslogtreecommitdiff
path: root/voctocore/lib/shmsrc.py
blob: 4e3c402f1fa1efeaacbf1de893258bcd2663a1be (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. last_restart_retry = 0
  9. is_in_failstate = True
  10. def __init__(self, socket, caps, failsrc):
  11. super().__init__()
  12. # Create elements
  13. self.shmsrc = Gst.ElementFactory.make('shmsrc', None)
  14. self.depay = Gst.ElementFactory.make('gdpdepay', None)
  15. self.capsfilter = Gst.ElementFactory.make('capsfilter', None)
  16. self.failsrcsyncer = Gst.ElementFactory.make('identity', None)
  17. self.switch = Gst.ElementFactory.make('input-selector', None)
  18. self.failsrc = failsrc
  19. self.capsstr = caps.to_string()
  20. if not self.shmsrc or not self.capsfilter or not self.failsrcsyncer or not self.switch or not self.failsrc:
  21. self.log.error('could not create elements')
  22. # Add elements to Bin
  23. self.add(self.shmsrc)
  24. self.add(self.depay)
  25. self.add(self.capsfilter)
  26. self.add(self.failsrcsyncer)
  27. self.add(self.switch)
  28. self.add(self.failsrc)
  29. # Get Switcher-Pads
  30. self.goodpad = self.switch.get_request_pad('sink_%u')
  31. self.failpad = self.switch.get_request_pad('sink_%u')
  32. # Set properties
  33. self.shmsrc.set_property('socket-path', socket)
  34. self.shmsrc.link(self.depay)
  35. self.switch.set_property('active-pad', self.failpad)
  36. self.failsrcsyncer.set_property('sync', True)
  37. self.capsfilter.set_property('caps', caps)
  38. # Link elements
  39. self.depay.link(self.capsfilter)
  40. self.capsfilter.get_static_pad('src').link(self.goodpad)
  41. self.failsrc.link_filtered(self.failsrcsyncer, caps)
  42. self.failsrcsyncer.get_static_pad('src').link(self.failpad)
  43. # Install pad probes
  44. self.shmsrc.get_static_pad('src').add_probe(Gst.PadProbeType.BLOCK | Gst.PadProbeType.EVENT_DOWNSTREAM, self.event_probe, None)
  45. self.shmsrc.get_static_pad('src').add_probe(Gst.PadProbeType.BLOCK | Gst.PadProbeType.BUFFER, self.data_probe, None)
  46. # Install Watchdog
  47. if self.capsstr.startswith('audio'):
  48. timeoutms = 1000
  49. else:
  50. timeoutms = 250
  51. GLib.timeout_add(timeoutms, self.watchdog)
  52. # Add Ghost Pads
  53. self.add_pad(
  54. Gst.GhostPad.new('src', self.switch.get_static_pad('src'))
  55. )
  56. def do_handle_message(self, msg):
  57. if msg.type == Gst.MessageType.ERROR and msg.src == self.shmsrc:
  58. (err, debug) = msg.parse_error()
  59. self.log.warning('received error-message from ShmSrc, dropping: %s', err)
  60. self.log.debug(' debug-info from shmsrc: %s', debug)
  61. else:
  62. Gst.Bin.do_handle_message(self, msg)
  63. def event_probe(self, pad, info, ud):
  64. e = info.get_event()
  65. if e.type == Gst.EventType.EOS:
  66. self.log.warning('received EOS-event on event-probe, dropping')
  67. self.switch_to_failstate()
  68. return Gst.PadProbeReturn.DROP
  69. return Gst.PadProbeReturn.PASS
  70. def data_probe(self, pad, info, ud):
  71. self.last_buffer_arrived = time.time()
  72. self.switch_to_goodstate()
  73. return Gst.PadProbeReturn.PASS
  74. def watchdog(self):
  75. t = time.time()
  76. if self.last_buffer_arrived + 0.1 < t:
  77. self.log.warning('watchdog encountered a timeout')
  78. self.switch_to_failstate()
  79. if self.is_in_failstate and self.last_restart_retry + 1 < t:
  80. self.last_restart_retry = t
  81. self.restart()
  82. return True
  83. def restart(self):
  84. self.log.warning('restarting ShmSrc')
  85. self.shmsrc.set_state(Gst.State.NULL)
  86. self.shmsrc.set_base_time(self.get_parent().get_base_time())
  87. self.shmsrc.set_state(Gst.State.PLAYING)
  88. def switch_to_goodstate(self):
  89. if not self.is_in_failstate:
  90. return
  91. self.log.warning('switching output to goodstate')
  92. self.is_in_failstate = False
  93. self.switch.set_property('active-pad', self.goodpad)
  94. def switch_to_failstate(self):
  95. if self.is_in_failstate:
  96. return
  97. self.log.warning('switching output to failstate')
  98. self.is_in_failstate = True
  99. self.switch.set_property('active-pad', self.failpad)