GStreamer: textoverlay在播放时未动态更新

1 投票
1 回答
3381 浏览
提问于 2025-04-17 22:46

我想在视频画面上显示当前的CPU负载(视频源是 /dev/video0),我觉得 textoverlay 这个元素非常适合这个需求。 我构建了一个(看起来)可以工作的流程,但 textoverlay 一直显示的是最开始设置的值。

现在这个流程是这样的:

v4l2src > qtdemux > queue > ffmpegcolorspace > textoverlay > xvimagesink

代码看起来是这样的(我去掉了一些gtk窗口、线程处理的代码和其他信号处理的部分,只保留了相关的部分):

#!/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()

这个 cpu_load_calculator 一直在后台运行,在我设置新值之前,我用 get_property() 函数打印出之前的值,确认它设置得没问题。不过在实际的视频输出窗口中,它还是显示最初的值。 我该怎么做才能让 textoverlay 在视频窗口中也能正确更新呢?

1 个回答

2

问题在于你试图从不同的进程中更新文本覆盖层。而进程和线程不同,进程是在各自独立的地址空间中运行的。

你可以改用线程:

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

或者你可以在主线程中运行cpu_load_calculator循环。这是可行的,因为self.pipeline.set_state(gst.STATE_PLAYING)会在后台启动自己的线程。

所以这样就足够了:

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

# CPU stats calculator loop
self.cpu_load_calculator()

撰写回答