如何通过自定义事件手动使用 gst_app_src_push_buffer()

Den*_*kov 5 qt gstreamer c++11

我想使用 gstreamer 的 appsrc 元素将图像作为视频流发送到 gstreamer 的管道。我查看了 appsrc 示例,其中使用了基于时间的流格式。其中每个缓冲区的时间戳步长为 0.5 秒,“need-data”回调每 0.5 秒调用一次。

但就我而言,我不需要此功能,当图像准备好发送时,我需要直接使用 gst_app_src_push_buffer() (据我所知)。

我使用以下初始化代码(简化的伪代码):

void Foo::initializeGst()
{
    // Init gstreamer.
    ::gst_init(nullptr, nullptr);

    // Configure appsrc element.
    m_appsrc = ::gst_element_factory_make(
                "appsrc", "source");

    ::g_object_set(G_OBJECT(m_appsrc),
                   "stream-type", GST_APP_STREAM_TYPE_STREAM,
                   "is-live", true,
                   nullptr);

    // Configure appsrc caps.
    const auto caps = ::gst_caps_new_simple(
                "video/x-raw",
                "format", G_TYPE_STRING, "BGRA",
                "width", G_TYPE_INT, 800,
                "height", G_TYPE_INT, 600,
                "framerate", GST_TYPE_FRACTION, 0, 1,
                nullptr);

    ::g_object_set(G_OBJECT(m_appsrc),
                   "caps", caps,
                   nullptr);

    // Configure video convertor element.
    const auto conv = ::gst_element_factory_make(
                "videoconvert", "conv");

    // Configure video encoder element.
    const auto videoenc = ::gst_element_factory_make(
                "x264enc", "video_encoder");

    // Configure payloader element.
    const auto payloader = ::gst_element_factory_make(
                "rtph264pay", "payloader");

    ::g_object_set(G_OBJECT(payloader),
                   "config-interval", 60,
                   nullptr);

    // Configure udpsink element.
    const auto udpsink = ::gst_element_factory_make(
                "udpsink", "udpsink");

    ::g_object_set(G_OBJECT(udpsink),
                   "host", "127.0.0.1",
                   "port", 50666,
                   nullptr);

    // Build pipeline.
    m_pipeline = ::gst_pipeline_new("pipeline");

    ::gst_bin_add_many(GST_BIN(m_pipeline),
                       m_appsrc,
                       conv,
                       videoenc,
                       payloader,
                       udpsink,
                       nullptr);

    const auto result = ::gst_element_link_many(
                m_appsrc, conv, videoenc, payloader, udpsink, nullptr);

    if (!result) {
        qDebug() << "Unable to initialize the GST";
    } else {
        // Play.
        const auto status = ::gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
        qDebug() << "Status:" << status;
    }
}
Run Code Online (Sandbox Code Playgroud)

以及以下缓冲区访问代码(简化的伪代码):

void Foo::setImage(const QImage &image)
{
    GstBuffer *buffer = gst_buffer_new_and_alloc(m_image.byteCount());
    const auto bytesCopied = ::gst_buffer_fill(
                buffer, 0, m_image.constBits(), m_image.byteCount());

    const auto result = ::gst_app_src_push_buffer(
                GST_APP_SRC(m_appsrc), buffer);

    qDebug() << "Push result" << result << "for copied bytes"
             <<  bytesCopied;
}
Run Code Online (Sandbox Code Playgroud)

但是,什么也没有发生,我没有看到视频流,例如使用 VLC 播放器(带有 SDP 文件),并且一段时间后,应用程序崩溃了。

但是,如果我使用“need-data”信号和 GST_FORMAT_TIME 选项:

::g_object_set(G_OBJECT(appsrc),
               "stream-type", 0,
               "is-live", TRUE,
               "format", GST_FORMAT_TIME,
               nullptr);

g_signal_connect(appsrc, "need-data", G_CALLBACK(need_data_cb), nullptr);

static void need_data_cb(GstElement *appsrc, guint unused_size, gpointer user_data)
{
    static gboolean white = FALSE;
    static GstClockTime timestamp = 0;

    const guint size = 800 * 600 * 4;
    GstBuffer *buffer = ::gst_buffer_new_allocate(nullptr, size, nullptr);

    // This makes the image black/white.
    ::gst_buffer_memset(buffer, 0, white ? 0xff : 0x0, size);

    white = !white;

    GST_BUFFER_PTS(buffer) = timestamp;
    GST_BUFFER_DURATION(buffer) = ::gst_util_uint64_scale_int (1, GST_SECOND, 20);

    timestamp += GST_BUFFER_DURATION(buffer);

    GstFlowReturn ret;
    ::g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret);

    ::gst_buffer_unref(buffer);
} 
Run Code Online (Sandbox Code Playgroud)

然后它就起作用了...

我根本不明白,我需要更改什么才能使其在没有时间戳和 GST_FORMAT_TIME 选项的情况下工作。

有人可以帮我吗?

BR,丹尼斯

Den*_*kov 9

啊..对不起,我笨了。

设置 GST_FORMAT_TIME 就足够了:

::g_object_set(G_OBJECT(appsrc),
               "stream-type", 0,
               "is-live", TRUE,
               "format", GST_FORMAT_TIME,
               nullptr);
Run Code Online (Sandbox Code Playgroud)

在创建缓冲区时,只需将当前时间戳值设置为缓冲区 pts 字段,例如使用 QElapsedTimer:

void Foo::setImage(const QImage &image)
{
    GstBuffer *buffer = gst_buffer_new_and_alloc(m_image.byteCount());
    const auto bytesCopied = ::gst_buffer_fill(
                buffer, 0, m_image.constBits(), m_image.byteCount());

    GST_BUFFER_PTS(buffer) = m_timer.nsecsElapsed();

    const auto result = ::gst_app_src_push_buffer(
                GST_APP_SRC(m_appsrc), buffer);

    qDebug() << "Push result" << result << "for copied bytes" 
             << bytesCopied;
}
Run Code Online (Sandbox Code Playgroud)

现在,一切正常,耶哈..:)