Вопрос

I wanted to see the current CPU load on top of the video image (source is /dev/video0), and I thought textoverlay element would be perfect for this. I have constructed a (seemingly) working pipeline, except that the textoverlay keeps showing the value originally set to it.

The pipeline is currently like this:

v4l2src > qtdemux > queue > ffmpegcolorspace > textoverlay > xvimagesink

And code looks like this (I have removed bunch of gtk window, thread handling code and some other signal handling, and only left the relevant part):

#!/usr/bin/env python

import sys, os, time, signal
import pygtk, gtk, gobject

import pygst
pygst.require("0.10")
import gst

# For cpu load stats
import psutil
from multiprocessing import Process, Value, Lock # For starting threads

class Video:
  def __init__(self):

    window = gtk.Window(gtk.WINDOW_TOPLEVEL)        
    vbox = gtk.VBox()
    window.add(vbox)
    self.movie_window = gtk.DrawingArea()
    vbox.add(self.movie_window)   
    window.show_all()

    # Set up the gstreamer pipeline
    self.pipeline = gst.Pipeline("pipeline")
    self.camera = gst.element_factory_make("v4l2src","camera")
    self.camera.set_property("device","""/dev/video0""")
    self.pipeline.add(self.camera)

    # Demuxer
    self.demuxer = gst.element_factory_make("qtdemux","demuxer")

    # Create a dynamic callback for the demuxer
    self.demuxer.connect("pad-added", self.demuxer_callback)

    self.pipeline.add(self.demuxer)  

    # Demuxer doesnt have static pads, but they are created at runtime, we will need a callback to link those
    self.videoqueue = gst.element_factory_make("queue","videoqueue")

    self.pipeline.add(self.videoqueue)

    self.videoconverter = gst.element_factory_make("ffmpegcolorspace","videoconverter")
    self.pipeline.add(self.videoconverter)

    ## Text overlay stuff
    self.textoverlay = gst.element_factory_make("textoverlay","textoverlay")

    self.overlay_text = "cpu load, initializing"
    self.textoverlay.set_property("text",self.overlay_text)
    self.textoverlay.set_property("halign", "left")
    self.textoverlay.set_property("valign", "top")
    self.textoverlay.set_property("shaded-background","true")
    self.pipeline.add(self.textoverlay)

    self.videosink = gst.element_factory_make("xvimagesink","videosink")
    self.pipeline.add(self.videosink)

    self.camera.link(self.videoqueue)
    gst.element_link_many(self.videoqueue,   self.videoconverter, self.textoverlay, self.videosink)


    bus = self.pipeline.get_bus()
    bus.add_signal_watch()
    bus.enable_sync_message_emission()

    # Start stream
    self.pipeline.set_state(gst.STATE_PLAYING)

    # CPU stats calculator thread    
    cpu_load_thread = Process(target=self.cpu_load_calculator, args=())
    cpu_load_thread.start()

  def demuxer_callback(self, dbin, pad):

    if pad.get_property("template").name_template == "video_%02d":
      print "Linking demuxer & videopad"
      qv_pad = self.videoqueue.get_pad("sink")
      pad.link(qv_pad)


  def cpu_load_calculator(self):

    cpu_num = len( psutil.cpu_percent(percpu=True))

    while True:
      load = psutil.cpu_percent(percpu=True)
      self.parsed_load = ""

      for i in range (0,cpu_num):
        self.parsed_load = self.parsed_load + "CPU%d: %s%% " % (i, load[i])

      print self.textoverlay.get_property("text") # Correctly prints previous cycle CPU load
      self.textoverlay.set_property("text",self.parsed_load)


      time.sleep(2)

c = Video()

gtk.threads_init()
gtk.main()

The cpu_load_calculator keeps running in the background, and before I set the new value, I print out the previous using the get_property() function, and it is set properly. However on the actual video outputwindow, it keeps to the initial value.. How can I make the textoverlay to update properly also to the video window ?

Это было полезно?

Решение

The problem is that you are trying to update textoverlay from different Process. And processes unlike threads run in separate address space.

You can switch to threads:

from threading import Thread
...
# CPU stats calculator thread    
cpu_load_thread = Thread(target=self.cpu_load_calculator, args=())
cpu_load_thread.start()

Or you can run cpu_load_calculator loop from the main thread. This will work because self.pipeline.set_state(gst.STATE_PLAYING) starts it's own thread in background.

So this will be enough:

# Start stream
self.pipeline.set_state(gst.STATE_PLAYING)

# CPU stats calculator loop
self.cpu_load_calculator()
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top