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;
    }
  • 相关阅读:
    LOJ 6089 小Y的背包计数问题 —— 前缀和优化DP
    洛谷 P1969 积木大赛 —— 水题
    洛谷 P1965 转圈游戏 —— 快速幂
    洛谷 P1970 花匠 —— DP
    洛谷 P1966 火柴排队 —— 思路
    51Nod 1450 闯关游戏 —— 期望DP
    洛谷 P2312 & bzoj 3751 解方程 —— 取模
    洛谷 P1351 联合权值 —— 树形DP
    NOIP2007 树网的核
    平面最近点对(加强版)
  • 原文地址:https://www.cnblogs.com/super119/p/1924412.html
Copyright © 2011-2022 走看看