aboutsummaryrefslogtreecommitdiff
path: root/voctocore/experiments/sync-videomix.py
blob: ce8eb1e3cb8fc3602db78f6abc792fddf6906df7 (plain)
  1. #!/usr/bin/python3
  2. import gi, time
  3. import socket
  4. # import GStreamer and GTK-Helper classes
  5. gi.require_version('Gst', '1.0')
  6. from gi.repository import GLib, Gst, GObject
  7. # init GObject before importing local classes
  8. GObject.threads_init()
  9. Gst.init(None)
  10. class Example:
  11. def __init__(self):
  12. self.mainloop = GObject.MainLoop()
  13. pipeline = """
  14. videotestsrc pattern=red !
  15. {caps} !
  16. videoscale !
  17. capsfilter name=caps0 !
  18. identity sync=true signal-handoffs=false !
  19. mix.
  20. videotestsrc pattern=green !
  21. {caps} !
  22. videoscale !
  23. capsfilter name=caps1 !
  24. identity sync=true signal-handoffs=false !
  25. mix.
  26. videomixer name=mix !
  27. {caps} !
  28. identity name=sig !
  29. videoconvert !
  30. pngenc !
  31. multifilesink location=frame%04d.png
  32. """.format(
  33. caps='video/x-raw,width=800,height=450,format=I420,framerate=25/1'
  34. )
  35. self.pipeline = Gst.parse_launch(pipeline)
  36. # with sync=False changes to videomixer & scaler are performed
  37. # from the main thread. In the frame-images it becomes clear, that
  38. # sometimes not all changes are applied before a frame is pushed
  39. # through the mixer, and so there are "half modified" frames where
  40. # some pieces are missing, the zorder or the size of one video is
  41. # incorrect
  42. # with sync=True all changes are made from the streaming-thread
  43. # that drives the videomixer and the following elements, so they
  44. # are always completed before the mixer processes the next frame
  45. sync = True
  46. if sync:
  47. sig = self.pipeline.get_by_name('sig')
  48. sig.connect('handoff', self.reconfigure)
  49. else:
  50. GLib.timeout_add(1/25 * 1000, self.reconfigure, 0, 0)
  51. self.pad0 = self.pipeline.get_by_name('mix').get_static_pad('sink_0')
  52. self.pad1 = self.pipeline.get_by_name('mix').get_static_pad('sink_1')
  53. self.caps0 = self.pipeline.get_by_name('caps0')
  54. self.caps1 = self.pipeline.get_by_name('caps1')
  55. self.state = False
  56. def reconfigure(self, object, buffer):
  57. print("reconfigure!")
  58. if self.state:
  59. padA = self.pad0
  60. padB = self.pad1
  61. capsA = self.caps0
  62. capsB = self.caps1
  63. else:
  64. padA = self.pad1
  65. padB = self.pad0
  66. capsA = self.caps1
  67. capsB = self.caps0
  68. padA.set_property('xpos', 10)
  69. padA.set_property('ypos', 10)
  70. padA.set_property('alpha', 1.0)
  71. padA.set_property('zorder', 1)
  72. capsA.set_property('caps', Gst.Caps.from_string('video/x-raw,width=320,height=180'))
  73. padB.set_property('xpos', 310)
  74. padB.set_property('ypos', 170)
  75. padB.set_property('alpha', 1.0)
  76. padB.set_property('zorder', 2)
  77. capsB.set_property('caps', Gst.Caps.from_string('video/x-raw,width=480,height=270'))
  78. self.state = not self.state
  79. return True
  80. def run(self):
  81. self.pipeline.set_state(Gst.State.PLAYING)
  82. self.mainloop.run()
  83. def kill(self):
  84. self.pipeline.set_state(Gst.State.NULL)
  85. self.mainloop.quit()
  86. example = Example()
  87. example.run()