I want to create a player class to play music with gst. This ist the code:
from gi.repository import GObject
GObject.threads_init()
import gst
class PlayerControl:
def __init__(self, main_window):
self.player = gst.element_factory_make("playbin2", "player")
self.sink = gst.element_factory_make("autoaudiosink", "player Output")
self.videosink= gst.element_factory_make("fakesink", "player fake Output")
self.player.set_property("video-sink", self.videosink)
self.player.set_property("audio-sink", self.sink)
self.player_bus = self.player.get_bus()
self.player_bus.add_signal_watch()
self.player_bus.connect("message", self.on_messages)
self.player.connect("about-to-finish", self.on_finish)
self.main_window = main_window
self.main_window.seek_bar.connect("value-changed", self.on_slider_changed)
self.main_window.seek_bar.set_range(0, 100)
self.playing = False
def on_messages(self, bus, message):
print(message)
def play(self, file):
self.playing = True
self.player.set_state(gst.STATE_READY)
self.player.set_property("uri", "file:///"+str(file)) # örnek olsun diye
self.player.set_state(gst.STATE_PLAYING)
GObject.timeout_add(100, self.update_slider)
[...]
I'm calling this from another class like this to play sound:
from file import PlayerControl
class PlayerExample():
def __init__( self ):
self.player_gst = PlayerControl(self)
[...]
def play(self, file):
self.playing = True
self.player.set_state(gst.STATE_READY)
self.player.set_property("uri", "file:///"+str(file))
self.player.set_state(gst.STATE_PLAYING)
GObject.timeout_add(100, self.update_slider)
[...]
I've connected a gtk button to play function. When i clicked the button i take segmentation fault without any information.
Also if i remove self.player_bus.add_signal_watch() and self.player.connect("about-to-finish", self.on_finish) lines from my PlayerControl class, it works and play sound but i need to call on_finished function with "about-to-finish" signal.
Why it doesn't work?
GObject.threads_init() from gi.repository doesn't work with pygst "0.10"
you need to add:
import gobject
gobject.threads_init()
import gst
top of the player class.
Or use Gst-1.0 https://wiki.ubuntu.com/Novacut/GStreamer1.0
Related
Background
I'm looking for a way to change the window that my video is being rendered into. This is necessary because there are some situations where the window can be destroyed, for example when my application switches into fullscreen mode.
Code
When the canvas is realized, the video source and sink are connected. Then when the prepare-window-handle message is emitted, I store a reference to the VideoOverlay element that sent it. Clicking the "switch canvas" button calls set_window_handle(new_handle) on this element, but the video continues to render in the original canvas.
import sys
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gst', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gtk, Gst, GstVideo
Gst.init(None)
if sys.platform == 'win32':
import ctypes
PyCapsule_GetPointer = ctypes.pythonapi.PyCapsule_GetPointer
PyCapsule_GetPointer.restype = ctypes.c_void_p
PyCapsule_GetPointer.argtypes = [ctypes.py_object]
gdkdll = ctypes.CDLL('libgdk-3-0.dll')
gdkdll.gdk_win32_window_get_handle.argtypes = [ctypes.c_void_p]
def get_window_handle(widget):
window = widget.get_window()
if not window.ensure_native():
raise Exception('video playback requires a native window')
window_gpointer = PyCapsule_GetPointer(window.__gpointer__, None)
handle = gdkdll.gdk_win32_window_get_handle(window_gpointer)
return handle
else:
from gi.repository import GdkX11
def get_window_handle(widget):
return widget.get_window().get_xid()
class VideoPlayer:
def __init__(self, canvas):
self._canvas = canvas
self._setup_pipeline()
def _setup_pipeline(self):
# The element with the set_window_handle function will be stored here
self._video_overlay = None
self._pipeline = Gst.ElementFactory.make('pipeline', 'pipeline')
src = Gst.ElementFactory.make('videotestsrc', 'src')
video_convert = Gst.ElementFactory.make('videoconvert', 'videoconvert')
auto_video_sink = Gst.ElementFactory.make('autovideosink', 'autovideosink')
self._pipeline.add(src)
self._pipeline.add(video_convert)
self._pipeline.add(auto_video_sink)
# The source will be linked later, once the canvas has been realized
video_convert.link(auto_video_sink)
self._video_source_pad = src.get_static_pad('src')
self._video_sink_pad = video_convert.get_static_pad('sink')
self._setup_signal_handlers()
def _setup_signal_handlers(self):
self._canvas.connect('realize', self._on_canvas_realize)
bus = self._pipeline.get_bus()
bus.enable_sync_message_emission()
bus.connect('sync-message::element', self._on_sync_element_message)
def _on_sync_element_message(self, bus, message):
if message.get_structure().get_name() == 'prepare-window-handle':
self._video_overlay = message.src
self._video_overlay.set_window_handle(self._canvas_window_handle)
def _on_canvas_realize(self, canvas):
self._canvas_window_handle = get_window_handle(canvas)
self._video_source_pad.link(self._video_sink_pad)
def start(self):
self._pipeline.set_state(Gst.State.PLAYING)
window = Gtk.Window()
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
window.add(vbox)
canvas_box = Gtk.Box()
vbox.add(canvas_box)
canvas1 = Gtk.DrawingArea()
canvas1.set_size_request(400, 400)
canvas_box.add(canvas1)
canvas2 = Gtk.DrawingArea()
canvas2.set_size_request(400, 400)
canvas_box.add(canvas2)
player = VideoPlayer(canvas1)
canvas1.connect('realize', lambda *_: player.start())
def switch_canvas(btn):
handle = get_window_handle(canvas2)
print('Setting handle:', handle)
player._video_overlay.set_window_handle(handle)
btn = Gtk.Button(label='switch canvas')
btn.connect('clicked', switch_canvas)
vbox.add(btn)
window.connect('destroy', Gtk.main_quit)
window.show_all()
Gtk.main()
Problem / Question
Calling set_window_handle() a 2nd time seems to have no effect - the video continues to render into the original window.
I've tried setting the pipeline into PAUSED, READY, and NULL state before calling set_window_handle(), but that didn't help.
I've also tried to replace the autovideosink with a new one as seen here, but that doesn't work either.
How can I change the window handle without disrupting the playback too much? Do I have to completely re-create the pipeline?
Looking at the source code, it appears that at least GL-based implementations of VideoOverlay element update the window id on expose event.
So you could try calling:
player._video_overlay.expose()
to reinitialize the GL scene after the window handle has been changed.
If that does not work, you can create a new VideoOverlay element and add it dynamically without stopping the graph.
i am writing an Indicator in Python which should update every 5 minutes. Unfortunately the label is updated only 3 times, but then the label stays the same, although the thread still runs.
For demonstration purpose, I replaced the data with the current time.
#!/usr/bin/env python3
import signal
import gi
import threading
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GObject
import time
class Indicator():
def __init__(self):
self.app = 'turtle-mining-indicator'
self.menu = {}
self.indicator = AppIndicator3.Indicator.new(
self.app, ICON,
AppIndicator3.IndicatorCategory.OTHER)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
self.indicator.set_menu(self.create_menu())
self.indicator.set_label("Starting ...", self.app)
self.scheduler()
def update(self):
print("update " + time.asctime())
self.indicator.set_label(time.asctime(), self.app)
self.menu["first"].set_label("First: " + time.asctime())
def scheduler(self):
self.update()
self.timer = threading.Timer(3, self.scheduler).start()
def create_menu(self):
menu = Gtk.Menu()
self.menu["first"] = Gtk.MenuItem("First")
menu.append(self.menu["first"])
menu.show_all()
return menu
Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
I don't understand, why this happens. The method update() gets executed but the set_label method only works 3 times. What am I doing wrong?
Mixing threads and Gtk is just plain wrong. GLib.timeout_add_seconds() is made specifically for this reason. Here is the proper way to do timely updates:
#!/usr/bin/env python3
import signal
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GLib
import time
class Indicator():
def __init__(self):
self.app = 'turtle-mining-indicator'
self.menu = {}
self.indicator = AppIndicator3.Indicator.new(
self.app, "my indicator",
AppIndicator3.IndicatorCategory.OTHER)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
self.indicator.set_menu(self.create_menu())
self.indicator.set_label("Starting ...", self.app)
GLib.timeout_add_seconds (1, self.update)
def update(self):
print("update " + time.asctime())
self.indicator.set_label(time.asctime(), self.app)
self.menu["first"].set_label("First: " + time.asctime())
return True
def create_menu(self):
menu = Gtk.Menu()
self.menu["first"] = Gtk.MenuItem("First")
menu.append(self.menu["first"])
menu.show_all()
return menu
Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
I need a python coding to monitor mouse coordinates in real-time in Linux. I found below coding and it works well, but the mouse coordinates information will be shown in a pops up GUI window. Now I don't need it show in a GUI window, just need save the mouse coordinates information to a text file(Log file). Does anyone can help me that? Thanks! Plus, it would be great if it can save mouse coordinates information by hourly.
#!/usr/bin/env python
"""
Corrected, the thread stops now.
"""
import sys
import os
from time import sleep
import gtk
gtk.gdk.threads_init()
import threading
# uses the package python-xlib
# from http://snipplr.com/view/19188/mouseposition-on-linux-via-xlib/
# or: sudo apt-get install python-xlib
from Xlib import display
old_stdout = sys.stdout
sys.stdout = open(os.devnull, 'w')
def mousepos():
"""mousepos() --> (x, y) get the mouse coordinates on the screen (linux, Xlib)."""
data = display.Display().screen().root.query_pointer()._data
return data["root_x"], data["root_y"]
class MouseThread(threading.Thread):
def __init__(self, parent, label):
threading.Thread.__init__(self)
self.label = label
self.killed = False
def run(self):
try:
while True:
if self.stopped():
break
text = "{0}".format(mousepos())
self.label.set_text(text)
sleep(0.01)
except (KeyboardInterrupt, SystemExit):
sys.exit()
def kill(self):
self.killed = True
def stopped(self):
return self.killed
class PyApp(gtk.Window):
def __init__(self):
super(PyApp, self).__init__()
self.set_title("Mouse coordinates 0.1")
self.set_size_request(250, 50)
self.set_position(gtk.WIN_POS_CENTER)
self.connect("destroy", self.quit)
label = gtk.Label()
self.mouseThread = MouseThread(self, label)
self.mouseThread.start()
fixed = gtk.Fixed()
fixed.put(label, 10, 10)
self.add(fixed)
self.show_all()
def quit(self, widget):
self.mouseThread.kill()
gtk.main_quit()
if __name__ == '__main__':
app = PyApp()
gtk.main()
Going over the code you provide. The information you want comes from def mousepos(). So with just
from Xlib import display
data = display.Display().screen().root.query_pointer()._data
data["root_x"], data["root_y"]
You get the current mouse position, put this in a loop and write/append to a file.
That should do the trick.
Having read the documentation for VPython and GTK threading, it seems to me that it would be possible to embed VPython graphics within a gtk GUI. I know that it is possible with wx on Windows but I am on Linux and using PyGTK. Now, I have managed to get part of the way. I can embed a VPython window provided that it is spawned a separate process. What I would like is to embed it as a thread. The latter would make GUI events that control the OpenGL easier to implement -- via a thread instead of a socket and network calls.
Edit: Apparently nobody knows anything about this... Meh.
Here is the code I have. Uncomment the two commented out lines and comment a few obvious others and you can get to the process spawning code.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
from visual import *
import threading
import Queue
import gtk
import pygtk
import re
import subprocess
class OPenGLThreadClass (threading.Thread):
"""Thread running the VPython code."""
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
self.name = 'OpenGLThread'
def run (self):
gtk.threads_enter()
self.scene = display.get_selected()
self.scene.title = 'OpenGL test'
s = sphere()
gtk.threads_leave()
#P = subprocess.Popen(['python', 'opengl.py'])
time.sleep(2)
self.queue.put(self.find_window_id())
self.queue.task_done()
def find_window_id (self):
"""Gets the OpenGL window ID."""
pattern = re.compile('0x[0-9abcdef]{7}')
P = subprocess.Popen(['xwininfo', '-name', self.scene.title],
#P = subprocess.Popen(['xwininfo', '-name', 'Visual WeldHead'],
stdout=subprocess.PIPE)
for line in P.stdout.readlines():
match = pattern.findall(line)
if len(match):
ret = long(match[0], 16)
print("OpenGL window id is %d (%s)" % (ret, hex(ret)))
return ret
class GTKWindowThreadClass (threading.Thread):
"""Thread running the GTK code."""
def __init__ (self, winID):
threading.Thread.__init__(self)
self.OpenGLWindowID = winID
self.name = 'GTKThread'
def run (self):
"""Draw the GTK GUI."""
gtk.threads_enter()
window = gtk.Window()
window.show()
socket = gtk.Socket()
socket.show()
window.add(socket)
window.connect("destroy", lambda w: gtk.main_quit())
print("Got winID as %d (%s)" % (self.OpenGLWindowID, hex(self.OpenGLWindowID)))
socket.add_id(long(self.OpenGLWindowID))
gtk.main()
gtk.threads_leave()
def main ():
thread = {}
print("Embedding OpenGL/VPython into GTK GUI")
queue = Queue.Queue()
thread['OpenGL'] = OPenGLThreadClass(queue)
thread['OpenGL'].start()
winID = queue.get()
print("Got winID as %d (%s)" % (winID, hex(winID)))
gtk.gdk.threads_init()
thread['GTK'] = GTKWindowThreadClass(winID)
thread['GTK'].start()
if __name__ == "__main__":
main()
This is the code that works in case anyone cares.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import subprocess
import sys
import os
import re
import time
from visual import *
def find_window_id (title):
"""Gets the OpenGL window ID."""
pattern = re.compile('0x[0-9abcdef]{7}')
proc = subprocess.Popen(['xwininfo', '-name', title],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
errors = proc.stderr.readlines()
if errors:
return None
for line in proc.stdout.readlines():
match = pattern.findall(line)
if len(match):
return long(match[0], 16)
return None
class Setting ():
"""VPython/OpenGL class."""
def __init__ (self, w=256, h=256, title='OpenGL via VPython'):
"""Initiator."""
self.width = w
self.height = h
self.title = title
self.scene = display.get_selected()
self.scene.title = self.title
self.scene.width = self.width
self.scene.height = self.height
self.sphere = sphere()
class GTKDisplay ():
def __init__ (self, winID):
"""Initiator: Draws the GTK GUI."""
import gtk
import pygtk
self.OpenGLWindowID = winID
window = gtk.Window()
window.show()
socket = gtk.Socket()
socket.show()
window.add(socket)
window.connect("destroy", lambda w: gtk.main_quit())
socket.add_id(long(self.OpenGLWindowID))
gtk.main()
def main ():
"""Main entry point."""
name = 'sphere OpenGL window'
child_pid = os.fork()
if 0 == child_pid:
sut = Setting(title=name)
else:
winID = None
while not winID:
time.sleep(.1)
winID = find_window_id(name)
try:
gui = GTKDisplay(winID)
except KeyboardInterrupt, err:
print '\nAdieu monde cruel!'
if __name__ == "__main__":
main()
Note: This does not work under Gnome but works under fvwm2. Go figure...
I have the following Python 2.7/PyGObject 3.0/PyGST 0.10 module:
from gi.repository import Gtk, Gdk, GdkPixbuf
import pango
import pygst
pygst.require('0.10')
import gst
import Trailcrest
import os, sys
import cairo
from math import pi
class Video:
def __init__(self):
def on_message(bus, message):
if message.type == gst.MESSAGE_EOS:
# End of Stream
player.seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, 5000000000, gst.SEEK_TYPE_NONE, 6000000000)
elif message.type == gst.MESSAGE_ERROR:
player.set_state(gst.STATE_NULL)
(err, debug) = message.parse_error()
print "Error: %s" % err, debug
def on_sync_message(bus, message):
if message.structure is None:
return False
if message.structure.get_name() == "prepare-xwindow-id":
Gdk.threads_enter()
print "Run before"
Gdk.Display.get_default().sync()
print "Run after"
win_id = videowidget.window.xid
imagesink = message.src
imagesink.set_property("force-aspect-ratio", True)
imagesink.set_xwindow_id(win_id)
Gtk.gdk.threads_leave()
def click_me(event, data=None):
player.seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, 5000000000, gst.SEEK_TYPE_NONE, 6000000000)
win = Gtk.Window()
win.set_resizable(False)
win.set_decorated(False)
win.set_position(Gtk.WindowPosition.CENTER)
fixed = Gtk.Fixed()
win.add(fixed)
fixed.show()
videowidget = Gtk.DrawingArea()
fixed.put(videowidget, 0, 0)
videowidget.set_size_request(640, 480)
videowidget.show()
# Setup GStreamer
player = gst.element_factory_make("playbin", "MultimediaPlayer")
bus = player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
#used to get messages that GStreamer emits
bus.connect("message", on_message)
#used for connecting video to your application
bus.connect("sync-message::element", on_sync_message)
player.set_property("uri", "file://" + os.getcwd() + "/VID/BGA-HABT-001.ogv")
player.set_state(gst.STATE_PLAYING)
win.show()
def main():
Gdk.threads_enter()
Gtk.main()
return 0
if __name__ == "__main__":
Video()
main()
I am always getting this error, along with the video opening in a new window, instead of the existing window.
Traceback (most recent call last): File "video.py", line 32, in
on_sync_message
win_id = videowidget.window.xid AttributeError: 'DrawingArea' object has no attribute 'window'
How do I fix this, so the video displays in the window I created, instead of a new one?
By the by, this problem only started occuring after I switched to PyGObject 3.0 from PyGTK 2.24.
(Reprinted from GNOME PyGObject 3 Bug Report 663360. Answer credit goes to Timo Vanwynsberghe).
There are a couple of things to note:
- the drawingarea has to be realised before you can get its GdkWindow
- apparently, you can't get the window property directly
- you need to import GdkX11 for the xid method
With this in mind, here is a minimal working example:
from gi.repository import GdkX11, Gtk
class App:
def __init__(self):
win = Gtk.Window()
win.resize(400, 400)
win.connect('delete-event', Gtk.main_quit)
da = Gtk.DrawingArea()
win.add(da)
win.show_all()
print da.get_property('window').get_xid()
if __name__ == "__main__":
App()
Gtk.main()