zoukankan      html  css  js  c++  java
  • GstBin和sink组件的状态转换代码分析和相关逻辑

    1. 每个element/bin都有current,next,pending三个成员变量表示状态。current和next很好理解,pending一般就是我们给该element设置的最终的状态,比如调用gst_element_set_state函数设置PLAYING state,则这个pending一般就是PLAYING。代码中还看到有一个宏GST_STATE_TARGET,这个TARGET一般也和pending一样,表示最终要设置的state(final state) 

    2. gst_element_set_state_func函数(gst_element_set_state的缺省实现)会调用gst_element_change_state,change_state又会调用gst_element_continue_state。对于这里state的转变,关键问题在sink element和bin上。对于sink element来说,只要到达PAUSED状态,就需要preroll,同时返回ASYNC,此时sink的状态不会commit,就是说还是READY和PLAYING状态,next state是PAUSED,pending状态是我们给他设置的最终状态。而且_set_state/change_state在看到返回了ASYNC之后,就直接返回了,也就是说,set_state函数调用到PAUSED之后就结束了。下面的工作就是gstbin完成的。这就又牵扯到gstbasesink.c和gstbin.c了。 

    basesink中,在状态变成PAUSED的时候,会发出ASYNC_START的消息,这个消息会被gstbin捕获(一般element都需要加到bin中,或是加到pipeline中)并记录,而且如果这个bin不是toplevel的话(所谓toplevel指的是这个bin的parent是NULL),还会将ASYNC_START消息继续往上发送,如果是toplevel就不会发了。此外,bin会修改自己的state, next, pending这些变量,注意只是修改这些变量,并不会去调用每个element的change_state函数。设置这些变量显示bin当前是PAUSED状态。 
    OK,basesink中,如果preroll完成(第一个非event的buffer到达,由函数gst_base_sink_preroll_object完成),就会调用gst_base_sink_commit_state函数,这个函数会直接将该sink的state设置成最终的那个state,也就是保存在pending中的state,同时将next和pending state都设成VOID,这表示sink现在preroll完成了,对于状态转换已经没有其他特别的东西了,然后发送ASYNC_DONE消息。gstbin在收到ASYNC_DONE消息后,首先查看是不是bin中所有发送过ASYNC_START的element都已经发送了DONE了,如果是,则调用关键函数bin_handle_async_done,这个函数会将bin的状态修改过来,然后会向gThreadPool中push一个task,这个task就会负责去调用bin的change_state函数,这个函数就会去调用每个element的change_state函数修改状态了。有关gthreadpool请参考glib中的内容。 

    所以,综合上面来看,在我们对一个sink element设置PLAYING状态时,最终是由bin来负责PAUSED->PLAYING的,因为sink element有一个preroll的过程,sink通过ASYNC_START和ASYNC_DONE来和bin进行通信。 

    为了验证上面的一点,我写了一个程序,这个程序在timeout callback函数中创建了一个fakesrc和一个udpsink,link他们,但是不把他们加入pipeline,最后设置他们的状态为PLAYING。同时,我在gstpipeline.c的change_state函数和gstbasesink.c的change_state函数中,在PAUSED->PLAYING状态时加入了一条打印语句,来监视这些代码是否会被触发。结果是,这两个element的状态变成了PLAYING,但是pipeline的change_state函数和basesink的change_state函数都没有被触发,这说明preroll完成后,只是修改了element的state的三个变量,真正的change_state函数需要由bin来负责调用。一旦将他们加入pipeline,就能发现change_state函数被调用了(指的是change_state中PAUSED->PLAYING部分的代码)。附件1中是测试的代码。 

    最后说一下,这次研究上面这些东西,是因为发现我加在rtspsrc中的代码会被反复调用。我在gst_rtspsrc_play中加入了一段代码,这段代码每次会创建四个element,2个fakesrc和2个udpsink。gst_rtspsrc_play会在rtspsrc由PAUSED转向PLAYING时被调用。结果发现我的代码被执行了很多次,导致创建出来了一大堆的element。原因就是上面的原理: 

    我每次创建的2个udpsink,设置成PLAYING后,发送ASYNC_START, ASYNC_DONE的消息,rtspsrc作为bin收到这些消息,在DONE消息处理的时候,会去调用change_state的PAUSED->PLAYING部分的代码,一调用这部分代码,我的代码又被执行,如此循环,导致创建出了很多的element。所以,关键就是: 

    a. 给创建的udpsink设置async属性为FALSE,这样就取消了preroll,就不会有ASYNC_START, ASYNC_DONE消息产生了。 
    或者 
    b. 检测是否已经创建了element,不要每次都创建element,如果已创建过,则无需再创建。 

    其他更严谨一些,创建element,尤其是sink element,根本就不应该放在转向PLAYING状态的时候,而是应该在PAUSED状态的时候就完成。 

     #include <gst/gst.h>


    GstElement 
    *pipeline, *fakesrc1, *udpsink1, *fakesrc2, *udpsink2;

    static gboolean on_timeout(gpointer data)
    {
        g_message(
    "ENTERING TIMEOUT PART  START...");

        fakesrc2 
    = gst_element_factory_make("fakesrc", NULL);
        g_object_set (G_OBJECT(fakesrc2), 
    "filltype"3, NULL);
        g_object_set (G_OBJECT(fakesrc2), 
    "num-buffers"5, NULL);
        
        gchar 
    *rtpuri = g_strdup_printf ("udp://202.119.24.12:5000");
        udpsink2 
    = gst_element_make_from_uri (GST_URI_SINK, rtpuri, NULL);
        g_free (rtpuri);

        
    // Add and link
        
    // gst_bin_add_many(GST_BIN(pipeline), fakesrc2, udpsink2, NULL);
        gst_element_link(fakesrc2, udpsink2);

        gst_element_set_state(fakesrc2, GST_STATE_PLAYING);
        gst_element_set_state(udpsink2, GST_STATE_PLAYING);
        
        GstState cur, pending;
        gst_element_get_state(fakesrc2, 
    &cur, &pending, GST_CLOCK_TIME_NONE);
        g_message(
    "fakesrc2 cur state is %d, pending state is %d", cur, pending);
        gst_element_get_state(udpsink2, 
    &cur, &pending, GST_CLOCK_TIME_NONE);
        g_message(
    "udpsink2 cur state is %d, pending state is %d", cur, pending);

        g_message(
    "ENTERING TIMEOUT PART  END...");
        
    return FALSE;
    }

    static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data)
    {
        GMainLoop 
    *loop = (GMainLoop *)data;
        
        
    switch(GST_MESSAGE_TYPE(msg)) {
            
    case GST_MESSAGE_EOS:
                g_print(
    "End-of-stream\n");
                g_print(
    "The message's owner is: %s\n", GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)));
                
    // g_main_loop_quit(loop);
                break;
            
    case GST_MESSAGE_ERROR: {
                gchar 
    *debug;
                GError 
    *err;

                gst_message_parse_error(msg, 
    &err, &debug);
                g_print(
    "Error debug info: %s\n", debug);
                g_free(debug);

                g_print(
    "Error: %s\n", err->message);
                g_error_free(err);

                g_main_loop_quit(loop);
                
    break;
            }
            
    case GST_MESSAGE_NEW_CLOCK: {
                GstClock 
    *newclock;
                gst_message_parse_new_clock(msg, 
    &newclock);
                g_message(
    "Got new clock, clock name is %s", GST_OBJECT_NAME(GST_OBJECT(newclock)));
                
    break;
            }
            
    case GST_MESSAGE_ASYNC_START: {
                g_message(
    "Got ASYNC_START message.");
                
    break;
            }
            
    case GST_MESSAGE_ASYNC_DONE: {
                g_message(
    "Got ASYNC_DONE message.");
                
    break;
            }
            
    default:
                
    break;
        }

        
    return TRUE;
    }

    int main(int argc, char *argv[])
    {
        GMainLoop 
    *loop;
        GstBus 
    *bus;

        gst_init(
    &argc, &argv);
        loop 
    = g_main_loop_new(NULL, FALSE);

        
    // create elements
        pipeline = gst_element_factory_make("pipeline""pipeline");
        fakesrc1 
    = gst_element_factory_make("fakesrc", NULL);
        g_object_set (G_OBJECT(fakesrc1), 
    "filltype"3, NULL);
        g_object_set (G_OBJECT(fakesrc1), 
    "num-buffers"5, NULL);
        
        gchar 
    *rtpuri = g_strdup_printf ("udp://202.119.24.12:5000");
        udpsink1 
    = gst_element_make_from_uri (GST_URI_SINK, rtpuri, NULL);
        g_free (rtpuri);

        
    if (!pipeline || !fakesrc1 || !udpsink1) {
            g_print(
    "Elements could not be created!\n");
            
    return -1;
        }

        bus 
    = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
        gst_bus_add_watch(bus, bus_call, loop);
        gst_object_unref(bus);

        
    // Add and link
        gst_bin_add_many(GST_BIN(pipeline), fakesrc1, udpsink1, NULL);
        gst_element_link(fakesrc1, udpsink1);

        
    // now play
        g_print("Setting to PLAYING\n");
        gst_element_set_state(pipeline, GST_STATE_PLAYING);
        g_print(
    "Running\n");

        
    // add timeout callback
        g_timeout_add(4000, (GSourceFunc)on_timeout, NULL);

        g_main_loop_run(loop);

        
    // clean up
        g_print("Returned, stopping playback\n");
        gst_element_set_state(pipeline, GST_STATE_NULL);
        g_print(
    "Deleting pipeline\n");

        gst_object_unref(GST_OBJECT(pipeline));

        
    return 0;
    }
  • 相关阅读:
    ROS2概述和实践入门
    ROS2与FastRTPSROS2与FastRTPS
    RoboWare Studio已经停更
    ubuntu20.04 Roboware安装遇到的问题
    Ubuntu16.04安装ROS2
    拥抱ROS2系列:ROS2概述和实践入门(一)
    clash for Linux安装使用
    Ubuntu 安装RoboWare
    Ubuntu 18.04 安装VSCode
    开源ImageFilter库For IOS源码发布
  • 原文地址:https://www.cnblogs.com/super119/p/1924412.html
Copyright © 2011-2022 走看看