summaryrefslogtreecommitdiff
path: root/voctocore/experiments/sync-videomix.py
blob: e49609195d028345919b307fbc2449fdd2b0a4b7 (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. mix.
  17. videotestsrc pattern=green !
  18. {caps} !
  19. mix.
  20. compositor name=mix !
  21. {caps} !
  22. identity name=sig !
  23. videoconvert !
  24. pngenc !
  25. multifilesink location=frame%04d.png
  26. """.format(
  27. caps='video/x-raw,width=800,height=450,format=I420,framerate=25/1'
  28. )
  29. self.pipeline = Gst.parse_launch(pipeline)
  30. # with sync=False changes to videomixer & scaler are performed
  31. # from the main thread. In the frame-images it becomes clear, that
  32. # sometimes not all changes are applied before a frame is pushed
  33. # through the mixer, and so there are "half modified" frames where
  34. # some pieces are missing, the zorder or the size of one video is
  35. # incorrect
  36. # with sync=True all changes are made from the streaming-thread
  37. # that drives the videomixer and the following elements, so they
  38. # are always completed before the mixer processes the next frame
  39. sync = True
  40. if sync:
  41. sig = self.pipeline.get_by_name('sig')
  42. sig.connect('handoff', self.reconfigure)
  43. else:
  44. GLib.timeout_add(1/25 * 1000, self.reconfigure, 0, 0)
  45. self.pad0 = self.pipeline.get_by_name('mix').get_static_pad('sink_0')
  46. self.pad1 = self.pipeline.get_by_name('mix').get_static_pad('sink_1')
  47. self.state = False
  48. def reconfigure(self, object, buffer):
  49. print("reconfigure!")
  50. if self.state:
  51. padA = self.pad0
  52. padB = self.pad1
  53. else:
  54. padA = self.pad1
  55. padB = self.pad0
  56. padA.set_property('xpos', 10)
  57. padA.set_property('ypos', 10)
  58. padA.set_property('alpha', 1.0)
  59. padA.set_property('zorder', 1)
  60. padA.set_property('width', 0)
  61. padA.set_property('height', 0)
  62. padB.set_property('xpos', 310)
  63. padB.set_property('ypos', 170)
  64. padB.set_property('alpha', 1.0)
  65. padB.set_property('zorder', 2)
  66. padB.set_property('width', 480)
  67. padB.set_property('height', 270)
  68. self.state = not self.state
  69. return True
  70. def run(self):
  71. self.pipeline.set_state(Gst.State.PLAYING)
  72. self.mainloop.run()
  73. def kill(self):
  74. self.pipeline.set_state(Gst.State.NULL)
  75. self.mainloop.quit()
  76. example = Example()
  77. example.run()