Python + QT + Gstreamer
Я работаю с PyQt и пытаюсь получить видео с веб-камеры для воспроизведения в виджете QT. Я нашел учебники для C и Qt, а также для python и gtk, но НИЧЕГО для этой комбинации из pyQt и gstreamer. Кто-нибудь, чтобы это работало?
Это хорошо воспроизводит видео, но в отдельном окне:
self.gcam = gst.parse_launch('v4l2src device=/dev/video0 ! autovideosink')
self.gcam.set_state(gst.STATE_PLAYING)
мне нужно, чтобы наложение работало так, чтобы оно отображалось в виджете моего графического интерфейса. Спасибо, гуру интернета!
хорошо, так что я получил намного дальше, но все еще нуждаюсь в некоторой помощи. На самом деле я пишу это для Maemo, но следующий код прекрасно работает на моем ноутбуке с Linux:
class Vid:
def __init__(self, windowId):
self.player = gst.Pipeline("player")
self.source = gst.element_factory_make("v4l2src", "vsource")
self.sink = gst.element_factory_make("autovideosink", "outsink")
self.source.set_property("device", "/dev/video0")
self.scaler = gst.element_factory_make("videoscale", "vscale")
self.window_id = None
self.windowId = windowId
self.player.add(self.source, self.scaler, self.sink)
gst.element_link_many(self.source,self.scaler, self.sink)
bus = self.player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect("message", self.on_message)
bus.connect("sync-message::element", self.on_sync_message)
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.player.set_state(gst.STATE_NULL)
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
self.player.set_state(gst.STATE_NULL)
def on_sync_message(self, bus, message):
if message.structure is None:
return
message_name = message.structure.get_name()
if message_name == "prepare-xwindow-id":
win_id = self.windowId
assert win_id
imagesink = message.src
imagesink.set_property("force-aspect-ratio", True)
imagesink.set_xwindow_id(win_id)
def startPrev(self):
self.player.set_state(gst.STATE_PLAYING)
print "should be playing"
vidStream = Vid(wId)
vidStream.startPrev()
где wId - идентификатор окна виджета, в котором я пытаюсь отобразить вывод. Когда я запускаю это на N900, экран становится черным и мигает. Есть идеи? Я умираю здесь!
РЕДАКТИРОВАТЬ: меня попросили опубликовать полный код, и хотя мне все еще нужно немного его почистить, вот соответствующая часть:
self.cameraWindow = QtGui.QWidget(self)
self.cameraWindow.setGeometry(QtCore.QRect(530, 20, 256, 192))
self.cameraWindow.setObjectName("cameraWindow")
self.cameraWindow.setAttribute(0, 1); # AA_ImmediateWidgetCreation == 0
self.cameraWindow.setAttribute(3, 1); # AA_NativeWindow == 3
global wId
wId = self.cameraWindow.winId()
self.camera = Vid(wId)
self.camera.startPrev()
class Vid:
def __init__(self, windowId):
self.player = gst.Pipeline("player")
self.source = gst.element_factory_make("v4l2src", "vsource")
self.sink = gst.element_factory_make("autovideosink", "outsink")
self.source.set_property("device", "/dev/video0")
#self.scaler = gst.element_factory_make("videoscale", "vscale")
self.fvidscale = gst.element_factory_make("videoscale", "fvidscale")
self.fvidscale_cap = gst.element_factory_make("capsfilter", "fvidscale_cap")
self.fvidscale_cap.set_property('caps', gst.caps_from_string('video/x-raw-yuv, width=256, height=192'))
self.window_id = None
self.windowId = windowId
print windowId
self.player.add(self.source, self.fvidscale, self.fvidscale_cap, self.sink)
gst.element_link_many(self.source,self.fvidscale, self.fvidscale_cap, self.sink)
bus = self.player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect("message", self.on_message)
bus.connect("sync-message::element", self.on_sync_message)
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.player.set_state(gst.STATE_NULL)
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
self.player.set_state(gst.STATE_NULL)
def on_sync_message(self, bus, message):
if message.structure is None:
return
message_name = message.structure.get_name()
if message_name == "prepare-xwindow-id":
win_id = self.windowId
assert win_id
imagesink = message.src
imagesink.set_property("force-aspect-ratio", True)
imagesink.set_xwindow_id(win_id)
def startPrev(self):
self.player.set_state(gst.STATE_PLAYING)
def pausePrev(self):
self.player.set_state(gst.STATE_NULL)
Это воедино несколько кусочков, и я не могу проверить это сейчас, но, возможно, это будет кому-то полезно. Удачи!
4 ответа
Понял! Похоже, мне нужно было настроить разрешение конвейера так, чтобы оно соответствовало разрешению виджета, куда я перекачивал видео:
self.fvidscale_cap = gst.element_factory_make ("capsfilter", "fvidscale_cap") self.fvidscale_cap.set_property ('caps', gst.caps_from_string ('video / x-raw-yuv, width = 256, height = 192'))
Затем просто добавьте их в конвейер, как и другие элементы, и это прекрасно работает. Чувак, мне сейчас так легко смотреть на это, но когда я несколько дней стучал головой о стену, это было не так очевидно...
Если вы собираетесь использовать PySide вместо PyQt на платформе, отличной от Linux, winId() возвращает PyCObject, который нельзя использовать напрямую с собственными функциями или другими модулями. В моем случае это пригодилось при использовании GStreamer (pygst) с PySide в Microsoft Windows:
from ctypes import pythonapi, c_void_p, py_object
...
if message_name == 'prepare-xwindow-id':
# convert winId from PyCObject to void pointer
pythonapi.PyCObject_AsVoidPtr.restype = c_void_p
pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
hWnd = pythonapi.PyCObject_AsVoidPtr(self.videoWidget.winId())
# set window handle to video sink
self.videoSink.set_xwindow_id(hWnd)
Ptterb Вы можете опубликовать свой полный код, пожалуйста?
Я скопировал твой код.
Добавлен fvidscale_cap в конвейер, с:
self.player.add(self.source, self.scaler, self.fvidscale_cap, self.sink)
gst.element_link_many(self.source,self.scaler, self.fvidscale_cap, self.sink)
Из основной программы я создаю новый QWidget и передаю его winId() в конструктор Vid.
Виджет начинает загружаться, но вылетает.
Выходные данные говорят:
должен играть
Ошибка сегментации
Вставленный код не показывает загрузку объекта, которая не может быть отклонена. Мне понадобилось время, чтобы выяснить, чего не хватает. Спасибо Джун за работающий аудио пример.
import gobject, pygst
pygst.require('0.10')
import gst
from PyQt4.QtGui import QMainWindow, QWidget, QApplication
import sys
class Video(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
container = QWidget()
self.setCentralWidget(container)
self.windowId = container.winId()
self.setGeometry(300,300,640,480)
self.show()
def setUpGst(self):
self.player = gst.Pipeline("player")
source = gst.element_factory_make("v4l2src", "vsource")
sink = gst.element_factory_make("xvimagesink", "sink")
fvidscale_cap = gst.element_factory_make("capsfilter", "fvidscale_cap")
fvidscale = gst.element_factory_make("videoscale", "fvidscale")
caps = gst.caps_from_string('video/x-raw-yuv')
fvidscale_cap.set_property('caps', caps)
source.set_property("device", "/dev/video0")
self.player.add(source, fvidscale, fvidscale_cap, sink)
gst.element_link_many(source,fvidscale, fvidscale_cap, sink)
bus = self.player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect("message", self.on_message)
bus.connect("sync-message::element", self.on_sync_message)
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.player.set_state(gst.STATE_NULL)
print "end of message"
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
self.player.set_state(gst.STATE_NULL)
def on_sync_message(self, bus, message):
if message.structure is None:
return
message_name = message.structure.get_name()
if message_name == "prepare-xwindow-id":
win_id = self.windowId
assert win_id
imagesink = message.src
imagesink.set_xwindow_id(win_id)
def startPrev(self):
self.player.set_state(gst.STATE_PLAYING)
print "should be playing"
if __name__ == "__main__":
gobject.threads_init()
app = QApplication(sys.argv)
video = Video()
video.setUpGst()
video.startPrev()
sys.exit(app.exec_())