[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