summaryrefslogtreecommitdiff
path: root/voctocore/experiments/failovertest2.py
blob: 19a883395c6546ece1d95842144781a4f7eeab75 (plain)
  1. #!/usr/bin/python3
  2. from os import path
  3. import threading
  4. import gi
  5. gi.require_version('Gst', '1.0')
  6. from gi.repository import GObject, Gst
  7. GObject.threads_init()
  8. Gst.init(None)
  9. class FramegrabberSource(Gst.Bin):
  10. def __init__(self, uri):
  11. super().__init__()
  12. # Create elements
  13. self.http = Gst.ElementFactory.make('souphttpsrc', None)
  14. self.demux = Gst.ElementFactory.make('multipartdemux', None)
  15. self.parse = Gst.ElementFactory.make('jpegparse', None)
  16. self.dec = Gst.ElementFactory.make('jpegdec', None)
  17. # Add elements to Bin
  18. self.add(self.http)
  19. self.add(self.parse)
  20. self.add(self.demux)
  21. self.add(self.dec)
  22. # Set properties
  23. self.http.set_property('location', uri)
  24. self.http.set_property('timeout', 5)
  25. #self.http.set_property('is-live', True)
  26. #self.http.set_property('do-timestamp', True)
  27. # Connect signal handlers
  28. self.demux.connect('pad-added', self.on_demux_pad_added)
  29. # Link elements
  30. self.http.link(self.demux)
  31. # dynamic linked # self.demux.link(self.parse)
  32. self.parse.link(self.dec)
  33. # Add Ghost Pads
  34. self.add_pad(
  35. Gst.GhostPad.new('src', self.dec.get_static_pad('src'))
  36. )
  37. def on_demux_pad_added(self, element, pad):
  38. string = pad.query_caps(None).to_string()
  39. print('on_demux_pad_added():', string)
  40. if string.startswith('image/jpeg'):
  41. pad.link(self.parse.get_static_pad('sink'))
  42. class FailoverFilter(Gst.Bin):
  43. failcount = 1
  44. def __init__(self):
  45. super().__init__()
  46. self.queue = Gst.ElementFactory.make('queue', None)
  47. self.failsrc = Gst.ElementFactory.make('videotestsrc', None)
  48. self.capsfilter = Gst.ElementFactory.make('capsfilter', None)
  49. self.switch = Gst.ElementFactory.make('input-selector', "sw")
  50. # Add elements to Bin
  51. self.add(self.queue)
  52. self.add(self.failsrc)
  53. self.add(self.capsfilter)
  54. self.add(self.switch)
  55. # Request Pads
  56. self.goodpad = self.switch.get_request_pad('sink_%u')
  57. self.failpad = self.switch.get_request_pad('sink_%u')
  58. # Set properties
  59. self.switch.set_property('active-pad', self.failpad)
  60. self.capsfilter.set_property('caps', Gst.Caps.from_string('video/x-raw,format=I420,width=640,height=480'))
  61. # Connect signal handlers
  62. self.queue.connect('underrun', self.underrun)
  63. self.queue.connect('overrun', self.overrun)
  64. self.queue.connect('running', self.running)
  65. self.queue.connect('pushing', self.pushing)
  66. # Link elements
  67. self.queue.get_static_pad('src').link(self.goodpad)
  68. self.failsrc.link(self.capsfilter)
  69. self.capsfilter.get_static_pad('src').link(self.failpad)
  70. # Add Ghost Pads
  71. self.add_pad(
  72. Gst.GhostPad.new('sink', self.queue.get_static_pad('sink'))
  73. )
  74. self.add_pad(
  75. Gst.GhostPad.new('src', self.switch.get_static_pad('src'))
  76. )
  77. # Start watchdog
  78. self.watchdog()
  79. def watchdog(self):
  80. self.watchdogtimer = threading.Timer(0.5, self.watchdog)
  81. self.watchdogtimer.start()
  82. if self.failcount == 0:
  83. print("watchdog switching to goodpad")
  84. self.switch.set_property('active-pad', self.goodpad)
  85. else:
  86. print("watchdog switching to failpad")
  87. self.switch.set_property('active-pad', self.failpad)
  88. def underrun(self, queue):
  89. level = queue.get_property('current-level-buffers')
  90. failbin = queue.get_parent()
  91. if level == 0:
  92. failbin.failcount += 1
  93. print('underrun(), level=', level, 'failcount=', failbin.failcount)
  94. def overrun(self, queue):
  95. level = queue.get_property('current-level-buffers')
  96. failbin = queue.get_parent()
  97. if level > 0:
  98. failbin.failcount = 0
  99. print('overrun(), level=', level, 'failcount=', failbin.failcount)
  100. def running(self, queue):
  101. level = queue.get_property('current-level-buffers')
  102. print('running(), level=', level)
  103. def pushing(self, queue):
  104. level = queue.get_property('current-level-buffers')
  105. failbin = queue.get_parent()
  106. if level == 0:
  107. failbin.failcount += 1
  108. print('pushing(), level=', level, 'failcount=', failbin.failcount)
  109. class VideomixerWithDisplay(Gst.Bin):
  110. def __init__(self):
  111. super().__init__()
  112. # Create elements
  113. self.secondsrc = Gst.ElementFactory.make('videotestsrc', None)
  114. self.mixer = Gst.ElementFactory.make('videomixer', None)
  115. self.q1 = Gst.ElementFactory.make('queue', None)
  116. self.q2 = Gst.ElementFactory.make('queue', None)
  117. self.display = Gst.ElementFactory.make('ximagesink', None)
  118. # Add elements to Bin
  119. self.add(self.secondsrc)
  120. self.add(self.mixer)
  121. self.add(self.display)
  122. self.add(self.q1)
  123. self.add(self.q2)
  124. # Set properties
  125. self.secondsrc.set_property('pattern', 'ball')
  126. # Request Pads
  127. self.firstpad = self.mixer.get_request_pad('sink_%u')
  128. self.secondpad = self.mixer.get_request_pad('sink_%u')
  129. # Set pad-properties
  130. self.secondpad.set_property('alpha', 0.7)
  131. self.secondpad.set_property('xpos', 50)
  132. self.secondpad.set_property('ypos', 50)
  133. # Link elements
  134. self.q1.get_static_pad('src').link(self.firstpad)
  135. self.q2.get_static_pad('src').link(self.secondpad)
  136. self.secondsrc.link(self.q2)
  137. self.mixer.link(self.display)
  138. # Add Ghost Pads
  139. self.add_pad(
  140. Gst.GhostPad.new('sink', self.q1.get_static_pad('sink'))
  141. )
  142. class Example:
  143. def __init__(self):
  144. self.mainloop = GObject.MainLoop()
  145. self.pipeline = Gst.Pipeline()
  146. self.bus = self.pipeline.get_bus()
  147. self.bus.add_signal_watch()
  148. self.bus.connect('message::eos', self.on_eos)
  149. self.bus.connect('message::error', self.on_error)
  150. # Create elements
  151. self.grabbersrc = FramegrabberSource(
  152. uri='http://beachcam.kdhnc.com/mjpg/video.mjpg?camera=1')
  153. self.failover = FailoverFilter()
  154. self.mixdisplay = VideomixerWithDisplay()
  155. # Add elements to pipeline
  156. self.pipeline.add(self.grabbersrc)
  157. self.pipeline.add(self.failover)
  158. self.pipeline.add(self.mixdisplay)
  159. self.grabbersrc.link(self.failover)
  160. self.failover.link(self.mixdisplay)
  161. def run(self):
  162. self.pipeline.set_state(Gst.State.PLAYING)
  163. self.mainloop.run()
  164. def kill(self):
  165. self.pipeline.set_state(Gst.State.NULL)
  166. self.mainloop.quit()
  167. def on_eos(self, bus, msg):
  168. print('on_eos()')
  169. #self.kill()
  170. def on_error(self, bus, msg):
  171. print('on_error():', msg.parse_error())
  172. #self.kill()
  173. example = Example()
  174. example.run()