简体   繁体   English

gstreamer:多个 RTSP 客户端同时连接导致视频流崩溃

[英]gstreamer: multiple RTSP clients connecting at the same time makes the video stream crash

Quick summary快速总结

video stream crashes if multiple clients connect at the same time due to the clients (all but 1) that skip the media-configure callback trying to change the bitrate by accessing a not yet configured pipeline.如果多个客户端同时连接,视频流会崩溃,因为客户端(除了 1 个)跳过媒体配置回调尝试通过访问尚未配置的管道来更改比特率。 I'm asking how to wait with calling change_bitrate as long as the configure-media callback hasn't yet finished.我在问,只要configure-media回调尚未完成,如何等待调用change_bitrate

Detailed overview详细概述

I'm developing a door phone application that shows video footage of a user (that just rang the door) over the RTSP protocol on one or multiple screens (called clients from now on) in eg an appartment building.我正在开发一个门电话应用程序,该应用程序通过 RTSP 协议在一个或多个屏幕(从现在开始称为客户端)上显示用户(刚刚按门)的视频片段,例如公寓楼。

When the application is running, it will not create a pipeline before the first client has connected.当应用程序运行时,它不会在第一个客户端连接之前创建管道。 A new client callback is created in the following way:通过以下方式创建一个新的客户端回调:

    /* Configure Callbacks */
    /* Create new client handler (Called on new client connect) */
    LOG_debug("Creating 'client-connected' signal handler");
    g_signal_connect(info.server, "client-connected", G_CALLBACK(new_client_handler), &info);

Which calls this function as soon as a client has connected:客户端连接后立即调用此函数:

/**
 * new_client_handler
 * Called by rtsp server on a new client connection
 */
static void new_client_handler(GstRTSPServer *server, GstRTSPClient *client, struct stream_info *si)
{
    DEBUG_ENTER;

    /* Used to initiate the media-configure callback */
    static gboolean first_run = TRUE;

    GstRTSPConnection *connection = gst_rtsp_client_get_connection(client);
    if (connection == NULL)
    {
        LOG_err("Could not get RTSP connection");
        DEBUG_EXIT;
        return;
    }

    GstRTSPUrl *url = gst_rtsp_connection_get_url(connection);
    if (url == NULL)
    {
        LOG_err("Could not get RTSP connection URL");
        DEBUG_EXIT;
        return;
    }


    si->num_cli++;

    gchar* uri = gst_rtsp_url_get_request_uri(url);
    LOG_info("[%d]A new client %s has connected", si->num_cli, uri);
    g_free(uri);

    si->connected = TRUE;

    /* Create media-configure handler */
    /*relevant part for question*/
    if (si->num_cli == 1)
    { /* Initial Setup */

        /**
         * Stream info is required, which is only
         * available on the first connection. Stream info is created
         * upon the first connection and is never destroyed after that.
         */
        if (first_run == TRUE)
        {
            LOG_debug("Creating 'media-configure' signal handler");
            g_signal_connect(si->factory, "media-configure", G_CALLBACK(media_configure_handler),
                    si);
        }
    }
    else
    {
        change_bitrate(si); //This makes video stream crash if 'media_configure_handler' isn't yet finished
    }

    /* Create new client_close_handler */
    LOG_debug("Creating 'closed' signal handler");
    g_signal_connect(client, "closed", G_CALLBACK(client_close_handler), si);

    first_run = FALSE;
    DEBUG_EXIT;
}

When a client is the first one to connect, it sets up the media-configure callback to initialize the pipeline.当客户端是第一个连接的客户端时,它会设置media-configure回调来初始化管道。 The configuration code looks like this:配置代码如下所示:

**
 * media_configure_handler
 * Setup pipeline when the stream is first configured
 */
static void media_configure_handler(GstRTSPMediaFactory *factory, GstRTSPMedia *media,
        struct stream_info *si)
{
    DEBUG_ENTER;

    si->media = media;

    LOG_info("[%d]Configuring pipeline...", si->num_cli);

    si->pipeline = GST_BIN(gst_rtsp_media_get_element(media)); //Pipeline gets configured here
    setup_elements(si);

    if (si->num_cli == 1)
    {
        /* Create Msg Event Handler */
        LOG_debug("Creating 'periodic message' handler");
        g_timeout_add(si->msg_rate * 1000, (GSourceFunc) periodic_msg_handler, si);
    }
    DEBUG_EXIT;
}

A second (or nth) client that connects skips the media configuration step and instead goes to change_bitrate .连接的第二个(或第 n 个)客户端跳过媒体配置步骤,而是转到change_bitrate Here the bitrate is adjusted based on the amount of connected clients.这里的比特率是根据连接的客户端的数量来调整的。

/**
 * change_bitrate
 * handle changing of bitrates
 */
static void change_bitrate(struct stream_info *si)
{
    DEBUG_ENTER;

    int c = si->curr_bitrate;
    int step = (si->max_bitrate - si->min_bitrate) / si->steps;
    GstElement *elem = search_pipeline(si->pipeline, "enc"); //crashes due to an unitialized pipeline
    const gchar *name = g_ascii_strdown(G_OBJECT_TYPE_NAME(elem), -1);
    GstStructure *extra_controls;
...
}

This all works fine if a single client connects first.如果单个客户端首先连接,这一切都可以正常工作。 Later, the connection can handle multiple clients and adjusts the bitrate accordingly.稍后,连接可以处理多个客户端并相应地调整比特率。

The problem arises if the first connection is by multiple clients : In this case, both clients enter an instance of new_client_handler , in which the first one will set up the media_configure_handler .如果第一个连接是由多个客户端进行的,则会出现问题:在这种情况下,两个客户端都输入一个new_client_handler实例,第一个将在其中设置media_configure_handler The second connection tries to change the bitrate , but fails because the pipeline is not yet configured by the callback.第二个连接尝试更改比特率,但由于回调尚未配置管道失败

How can i make the second (and nth) connection wait until the media configure callback has finished and thus a pipeline is available?如何使第二个(和第 n 个)连接等到媒体配置回调完成并因此管道可用?

Solved this in the end with the following code (in function new_client_handler )最后用以下代码解决了这个问题(在函数new_client_handler中)

    /* Create media-configure handler */
    if (si->num_cli == 1)
    { /* Initial Setup */

        /**
         * Stream info is required, which is only
         * available on the first connection. Stream info is created
         * upon the first connection and is never destroyed after that.
         */
        if (first_run == TRUE)
        {
            LOG_debug("Creating 'media-constructor' signal handler");
            g_signal_connect(si->factory, "media-constructed", G_CALLBACK(media_configure_handler),
                    si);
        }
    }
    else if(si->pipeline != 0)
    {
        change_bitrate(si);
    }
    else
    {
        g_signal_connect(si->factory, "media-configure", G_CALLBACK(media_constructed_handler),
                si);
    }

Pipeline object's construction is now hooked to the media-constructed event, which runs before the media-configure event.管道对象的构造现在与media-constructed事件挂钩,该事件在media-configure事件之前运行。 A second client will only change bitrate if pipeline is initialized.如果管道被初始化,第二个客户端只会改变比特率。 If not, the client hooks in the media-configure callback and changes bitrate there.如果没有,客户端会挂接media-configure回调并在那里更改比特率。 This callback is guaranteed to run after the media-constructed callback.此回调保证在media-constructed的回调之后运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM