[gst-devel] appsink, appsrc, pad problems oh my.
Rohan
rohan at perzonae.com
Fri Nov 6 11:25:44 CET 2009
Hey there everyone,
I have been messing with gstreamer for a bit now, and have a strange
experience. I managed to get a pipeline for a camera that goes
through an appsink and appsrc to work temporarily. It tended to be
flaky, and it seems to be a problem with pads, where in linking for
some reason the elements are trying to link a src pad to another src
pad, which not surprisingly does not work. I have tried to link the
pads manually, and it does not work.
What is strange is that the encoding, and putting into a buffer works
a treat with a gst.element_link_many doing the correct thing. Where
it is going wrong is on the other side with the appsrc, where it is
then setting videorate, and decoding it. When all goes well the image
from the camera displays in the wx window, with a bit of a lag, but at
least it is working.
I have been running this with --gst-debug-level=3 which is how I know
about the pads problem. I would love any suggestions anyone has,
because my experiments so far are not having any effect, and I fear I
am once again missing something very obvious.
The code for the script is below. The init has all the wx code, which
is very straight forward, and as much as possible I have tried to put
the various elements into their own bin, and own functions for
building the bins.
Thanks in advance for any help.
Rohan
P.S. This is very experimental, frankenstinian code with bits grafted
on from all over the map, so any layout/organization improvements are
also appreciated.
P.P.S. The problems seem to lie presently in the video_display method.
-------------------------------------------------------------------------
#!/usr/bin/env python
import wx
import pygst
pygst.require("0.10")
import gst
import gobject
gobject.threads_init()
class WX_Main(wx.App):
def OnInit(self):
### WX GUI STUFF
window = wx.Frame(None)
window.SetTitle("Video-Player")
window.SetSize((500, 400))
window.Bind(wx.EVT_CLOSE,self.destroy)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox = wx.BoxSizer(wx.HORIZONTAL)
# self.entry = wx.TextCtrl(window)
# hbox.Add(self.entry, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
self.vidstart = wx.Button(window,label="Start Video")
hbox.Add(self.vidstart, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
self.vidstart.Bind(wx.EVT_BUTTON, self.video_toggle)
self.soundstart = wx.Button(window, label="Start Sound")
hbox.Add(self.soundstart, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
self.soundstart.Bind(wx.EVT_BUTTON, self.sound_toggle)
self.bothstart = wx.Button(window, label="Start Both")
hbox.Add(self.bothstart, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
self.bothstart.Bind(wx.EVT_BUTTON, self.both_start)
self.bothstop = wx.Button(window, label="Stop Both")
hbox.Add(self.bothstop, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
self.bothstop.Bind(wx.EVT_BUTTON, self.both_stop)
self.quit = wx.Button(window, label="Quit")
hbox.Add(self.quit, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
self.quit.Bind(wx.EVT_BUTTON, self.exit)
vbox.Add(hbox, 0, wx.EXPAND, 0)
self.display_window = wx.Panel(window)
vbox.Add(self.display_window,1,wx.ALL|wx.EXPAND,4)
window.SetSizer(vbox)
window.Layout()
window.Show()
self.SetTopWindow(window)
#### GSTREAMER STUFF
self.streamheader = None
self.requests = []
self.vidplayer = self.buildvidplayer()
#-- appsink bus
bus = self.vidplayer.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)
#----------------
self.vidout = self.buildvidout()
#-- appsrc bus
bus2 = self.vidout.get_bus()
bus2.add_signal_watch()
bus2.enable_sync_message_emission()
bus2.connect('message', self.on_message2)
bus2.connect('sync-message::element', self.on_sync_message2)
self.buffer=[]
return True
def debug(self, message):
print message
def buildvidplayer(self):
player = gst.Pipeline('vidplayer')
camera = gst.element_factory_make("v4l2src", 'camera')
vidsrc = gst.element_factory_make("tee")
player.add(camera, vidsrc)
gst.element_link_many(camera, vidsrc)
self.camcaps = camera.get_pad('src').get_caps()
print "CAMERA CAPS: ", camera.get_pad('src').get_caps()
# streamed video
svidbin = self.stream_vidbin()
player.add(svidbin)
vidsrc.link(svidbin)
return player
def stream_vidbin(self):
bin = gst.Bin("streamvid")
# queue ! ffmpegcolorspace ! smokeenc keyframe=8 qmax=40 !
queue = gst.element_factory_make("queue", "queueSINK")
colourconv = gst.element_factory_make("ffmpegcolorspace", "ffmpegcspSINK")
videorate = gst.element_factory_make("videorate", "videorateSINK")
videoscale = gst.element_factory_make("videoscale", "videoscaleSINK")
theoraenc = gst.element_factory_make("theoraenc", "theoraencSINK")
#create = gst.element_factory_make("oggmux", "ogg-create")
out = self.vid_appsink()
## add and link
bin.add(queue, colourconv, videorate,videoscale, theoraenc, out)
gst.element_link_many(queue, colourconv, videorate,videoscale,
theoraenc,out)
# GhostPad for bin
binsink = gst.GhostPad("binsink", queue.get_pad("sink"))
bin.add_pad(binsink)
return bin
def vid_appsink(self):
""" Inspired by
https://coherence.beebits.net/svn/trunk/Coherence/coherence/transcoder.py"""
out = gst.element_factory_make("appsink", "sink")
out.set_property('emit-signals', True)
out.connect("new-preroll", self.new_preroll)
out.connect("new-buffer", self.new_buffer)
out.connect("eos", self.eos)
return out
def new_preroll(self, appsink):
print "prerolling"
buffer = appsink.emit('pull-preroll')
#print buffer
print "BUFFER PREROLL LENGTH: ", len(buffer)
self.streamheading(buffer)
def new_buffer(self, appsink):
print "new buffering"
buffer = appsink.emit('pull-buffer')
#print buffer
print "NEW BUFFER LENGTH: ", len(buffer)
self.streamheading(buffer)
def streamheading(self, buffer):
if not self.streamheader:
# check caps for streamheader buffers
caps = buffer.get_caps()
print "CAPS: ", caps
s = caps[0]
if s.has_key("streamheader"):
self.streamheader = s["streamheader"]
self.debug("setting streamheader")
for r in self.requests:
self.debug("writing streamheader")
for h in self.streamheader:
r.write(h.data)
print type(buffer)
self.buffer.append(buffer)
def need_image(self, src, length):
print "needing image"
#if self.buffer:
if self.buffer:
buffer = self.buffer.pop(0)
buffer.set_caps (self.appsrc.get_property ("caps"))
# buffer.timestamp = self._frame * 1000*1000*40
buffer.duration = 1000*1000*40
#buffer.surface = self.surface
self.appsrc.emit ("push-buffer", buffer)
#stream.emit ("end-of-stream")
def buildvidout(self):
player = gst.Pipeline('vidout')
#video source
vidsource = gst.element_factory_make("appsrc")
vidsource.set_property("caps", gst.Caps(self.camcaps))
#vidsource.set_property ("max-bytes", 10*1024*1024)
vidsource.connect ('need-data', self.need_image)
player.add(vidsource)
# video display
vidbin = self.video_display()
player.add(vidbin)
vidsource.link(vidbin)
self.appsrc=vidsource
return player
def video_display(self):
bin = gst.Bin("vidbin")
queue = gst.element_factory_make("queue", "queueSRC")
videorate = gst.element_factory_make("videorate", "videorateSRC")
videoscale = gst.element_factory_make("videoscale", "videoscaleSRC")
theoradec = gst.element_factory_make("theoradec", "theoradecSRC")
ffmpegcolorspace = gst.element_factory_make("ffmpegcolorspace",
"ffmpegcspSRC")
vidsink = gst.element_factory_make("ximagesink", "ximagesinkSRC")
self.__display_videosink=vidsink
bin.add(queue,
theoradec,videoscale,videorate,
ffmpegcolorspace, vidsink)
gst.element_link_many(queue, theoradec)
# this is giving a cannot link pads error
#theoradec.link_pads('sink', videoscale, 'src')
###
### This is where the problems seem to lie
###
# this is sometimes working, and other times giving the
# cannot connect src pad to src pad
theoradec.link(videoscale)
# need to link videoscaleSRC to videorateSRC pads manually
videoscale.link(videorate)
# need to link videorate and ffmpegcolorspace pads manually
videorate.link(ffmpegcolorspace)
ffmpegcolorspace.link(vidsink)
# ghostpad
binsink = gst.GhostPad("binsink", queue.get_pad("sink"))
bin.add_pad(binsink)
return bin
def eos(self, appsink):
self.debug("eos")
for r in self.requests:
r.finish()
###### Mostly wx related stuff, but calling and triggering gstreamer
###### stuff
def video_toggle(self, event):
self.toggle_media(self.vidstart, self.vidplayer)
def both_start(self, event):
self.toggle_media(self.vidstart, self.vidplayer, "Start")
self.toggle_media(self.soundstart, self.soundplayer, "Start")
def both_stop(self, event):
self.toggle_media(self.vidstart, self.vidplayer, "Stop")
self.toggle_media(self.soundstart, self.soundplayer, "Stop")
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.vidplayer.set_state(gst.STATE_NULL)
self.vidstart.SetLabel("Start")
elif t == gst.MESSAGE_ERROR:
self.vidplayer.set_state(gst.STATE_NULL)
self.vidstart.SetLabel("Start")
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':
imagesink = message.src
imagesink.set_property('force-aspect-ratio', True)
imagesink.set_xwindow_id(self.display_window.GetHandle())
def destroy(self,event):
#Stop the player pipeline to prevent a X Window System error
self.debug("pipeline cleanup")
self.vidplayer.set_state(gst.STATE_NULL)
self.requests = []
self.streamheader = None
event.Skip()
def exit(self, event):
self.debug("pipeline cleanup and exitting")
# Stop the player pipeline and close the App
self.vidplayer.set_state(gst.STATE_NULL)
self.requests = []
self.streamheader = None
self.Exit()
if __name__ == "__main__":
app = WX_Main()
app.MainLoop()
More information about the gstreamer-devel
mailing list