В gstreamer не могу удалить секцию тройника после EOS - PullRequest
0 голосов
/ 31 марта 2020

Я пытаюсь создать веб-камеру на встроенном устройстве и одновременно изучать реализацию gstreamer c. Я имел дело с конвейерами запуска gstreamer некоторое время, так что я уже немного знаком с gstreamer.

Моя конечная цель - в конечном итоге создать конвейер, который будет динамически передавать видео, записывать видео и сохранять все изображения из внешних команд. Я начал с моей реализации, и сейчас я сосредоточен на том, чтобы сделать снимок в одной ветви тройника, пока другая ветвь все еще передает данные. другая ветвь сейчас просто подделка, но в конечном итоге это будет кодер h264 с мультиплексированием и сохранением аудио-видео.

вот простой вид моего конвейера: v4l2sr c! Капсфильтр! тройник очередь ! тройник ! очередь ! видеоконверт! pngen c! filesink

Моя идея заключалась в том, чтобы динамически добавлять часть изображения конвейера во время его работы. поток моей программы выглядит следующим образом:
запускается событие изображения (в настоящее время это простой таймер) -> добавить блокирующий пробник на tee -> добавить конвейер изображения и связать его с tee -> установить для воспроизведения -> установить блокирующий пробник на fileink, чтобы проверить, что он получил данные -> отправить EOS по конвейеру, начиная с видеоконвертирования -> установить зонд блокировки на тройнике, связанном с конвейером изображения -> установить нулевой конвейер изображения и удалить его и тройник

когда программа выполняется, зонд eos на тройнике для конвейера изображения никогда не вызывается, и вместо этого весь конвейер переходит в EOS, и я получаю внутреннюю ошибку потока данных, но изображение не отображается.

Я хочу сделать уверен, что fileink получает только 1 буфер, так как я не могу остановить поток v4l2sr c или дать ему num-buffers = 1. Я думаю, что моя проблема сейчас: как я могу убедиться, что файловый прием получает только один буфер? на какой планшет следует отправлять событие EOS, чтобы оно правильно сохранило картинку? и, наконец, как я могу убедиться, что только эта ветвь видит EOS?

я изучил все учебные пособия по gstreamer и вопросы SO, но большинство из них либо не ответили, либо не помогли моей ситуации.

вот мой код:

#include <QDebug>
#include <QTimer>
#include "gstpipeline.hpp"
#include "gsttypes.hpp"

using namespace INSP_GST_TYPES;

gstpipeline::gstpipeline()
: mV4l2Src(NULL)
, mEncoder(NULL)
, mPngEncoder(NULL)
, mVideoFileSink(NULL)
, mPictureFileSink(NULL)
, mRawCapsFilter(NULL)
, mEncodedCapsFilter(NULL)
, mEncoderVideoConvert(NULL)
, mPngVideoConvert(NULL)
, mEncoderQueue(NULL)
, mMatroskaMux(NULL)
, mPipeline(NULL)
{
}

void gstpipeline::init()
{
    mV4l2Src = gst_element_factory_make("v4l2src", V4L2SOURCE_NAME);
    mRawCapsFilter = gst_element_factory_make("capsfilter", RAW_CAPS_NAME);
    mRawFakesinkQueue = gst_element_factory_make("queue", RAW_FAKESINK_QUEUE_NAME);
    mRawFakeSink = gst_element_factory_make("fakesink", RAW_FAKESINK_NAME);
    mRawTee = gst_element_factory_make("tee", RAW_TEE_NAME);
    mPipeline = gst_pipeline_new(PIPELINE_NAME);

    mRawCaps = gst_caps_new_simple("video/x-raw",
                                   "format", G_TYPE_STRING, "NV12",
                                   "width", G_TYPE_INT, 1280,
                                   "height", G_TYPE_INT, 720,
                                   "framerate", GST_TYPE_FRACTION, 30, 1,
                                   NULL);

    g_object_set(mRawCapsFilter, "caps", mRawCaps, NULL);

    if(!mPipeline || !mV4l2Src || !mRawCapsFilter || !mRawTee || !mRawFakesinkQueue || !mRawFakeSink)
    {
        qCritical() << "Failed to create main gst elements";
        return;
    }
    else
    {
        qWarning() << "created the initial pipeline";
    }

    linkRawPipeline();
}

void gstpipeline::linkRawPipeline()
{
    gst_bin_add_many(GST_BIN(mPipeline), mV4l2Src, mRawCapsFilter, mRawTee, mRawFakesinkQueue, mRawFakeSink, NULL);
    g_object_set(mPipeline, "message-forward", TRUE, NULL);
    if(gst_element_link_many(mV4l2Src, mRawCapsFilter, mRawTee, NULL) != TRUE)
    {
        qCritical() << "Failed to link raw pipeline";
        return;
    }
    if(gst_element_link_many(mRawFakesinkQueue, mRawFakeSink, NULL) != TRUE)
    {
        qCritical() << "Failed to link fakesink pipeline";
        return;
    }

    /* Manually link the Tee, which has "Request" pads */
    GstPad* tee_fakesink_pad = gst_element_get_request_pad (mRawTee, "src_%u");
    qWarning ("Obtained request pad %s for fakesink branch.", gst_pad_get_name (tee_fakesink_pad));
    GstPad* raw_queue_pad = gst_element_get_static_pad (mRawFakesinkQueue, "sink");
    if (gst_pad_link (tee_fakesink_pad, raw_queue_pad) != GST_PAD_LINK_OK)
    {
      qCritical ("raw Tee could not be linked.");
    }
    gst_object_unref(tee_fakesink_pad);
    gst_object_unref(raw_queue_pad);

    if (gst_element_set_state (mPipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
    {
        qCritical() << "Unable to set the pipeline to the ready state";
        gst_object_unref (mPipeline);
    }
    else
    {
        qWarning() << "set pipeline to playing";

        GMainLoop* loop = g_main_loop_new (NULL, FALSE);
        gst_bus_add_watch (GST_ELEMENT_BUS (mPipeline), sMainBusCallback, loop);

        QTimer::singleShot(1000, this, SLOT(onBusTimeoutExpired()));
    }
}

void gstpipeline::onBusTimeoutExpired()
{
    blockRawPipeline();
}

void gstpipeline::blockRawPipeline()
{
    qWarning() << "Blocking raw pipeline";
    GstPad* srcpad = gst_element_get_static_pad(mRawFakesinkQueue, SRC_PAD);

    gst_pad_add_probe(srcpad,
                (GstPadProbeType)(GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_IDLE),
                sRawFakesinkQueueBlockedCallback, NULL, NULL);

    g_object_unref(srcpad);
    qWarning() << "added fakesink queue probe";
}

GstPadProbeReturn gstpipeline::sRawFakesinkQueueBlockedCallback(GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
    gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));

    //create the picturesink pipeline and link it to a new src pad on the raw tee
    mPictureQueue = gst_element_factory_make("queue", RAW_PICTURE_QUEUE_NAME);
    mPngEncoder = gst_element_factory_make("pngenc", PNG_ENC_NAME);
    mPictureFileSink = gst_element_factory_make("filesink", PICTURESINK_NAME);
    mPngVideoConvert =  gst_element_factory_make("videoconvert", VIDEOCONVERT_PNG_NAME);

    if(!mPngEncoder || !mPictureFileSink || !mPngVideoConvert)
    {
        qCritical() << "failed to make picturesink elements";
    }

    g_object_set(G_OBJECT (mPictureFileSink), "location", "/mnt/userdata/pipelinetest.png", NULL);

    gst_bin_add_many (GST_BIN (mPipeline), mPictureQueue, mPngVideoConvert,
                      mPngEncoder, mPictureFileSink, NULL);

    if(gst_element_link_many(mPictureQueue, mPngVideoConvert, mPngEncoder, mPictureFileSink, NULL) != TRUE)
    {
        qCritical() << "failed to link picture pipeline";
    }

    GstPad* tee_picturesink_pad = gst_element_get_request_pad (mRawTee, "src_%u");
    qWarning ("Obtained request pad %s for picturesink branch.", gst_pad_get_name (tee_picturesink_pad));
    GstPad* raw_picture_queue_pad = gst_element_get_static_pad (mPictureQueue, "sink");
    if (gst_pad_link (tee_picturesink_pad, raw_picture_queue_pad) != GST_PAD_LINK_OK)
    {
      qCritical ("picture Tee could not be linked.");
    }

    gst_element_sync_state_with_parent(mPictureQueue);
    gst_element_sync_state_with_parent(mPngVideoConvert);
    gst_element_sync_state_with_parent(mPngEncoder);
    gst_element_sync_state_with_parent(mPictureFileSink);

    qWarning() << "done adding picturesink";

    //set data block to see when the filesink gets data so we can send an EOS
    GstPad* srcpad = gst_element_get_static_pad(mPictureFileSink, SINK_PAD);

    gst_pad_add_probe(srcpad,  (GstPadProbeType)(GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM),
                       sPictureSinkDownstreamBlockProbe, NULL, NULL);

    g_object_unref(srcpad);

    return GST_PAD_PROBE_DROP;
}

GstPadProbeReturn gstpipeline::sPictureSinkDownstreamBlockProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
{
    gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));

    //this is a data blocking pad probe on picture filesink
    qWarning() << "setting the EOS event probe on the picturesink";
    GstPad* srcpad = gst_element_get_static_pad(mPictureQueue, SRC_PAD);
    gst_pad_add_probe(pad, (GstPadProbeType)(GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM),sPictureSinkEOSCallback, NULL, NULL);
    g_object_unref(srcpad);

    qWarning() << "sending eos through videoconvert";
    gst_element_send_event(mPngVideoConvert, gst_event_new_eos());

    qWarning() << "exiting pad probe";
    return GST_PAD_PROBE_PASS;
}

GstPadProbeReturn gstpipeline::sPictureSinkEOSCallback(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
{
    gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));
    if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_DATA (info)) == GST_EVENT_EOS)
    {
        qWarning() << "setting raw queue pad block";
        GstPad* srcpad = gst_element_get_static_pad(mPictureQueue, SRC_PAD);
        gst_pad_add_probe(pad, (GstPadProbeType)(GST_PAD_PROBE_TYPE_IDLE),sRawQueueBlockedCallback, NULL, NULL);
        g_object_unref(srcpad);
    }
    else
    {
        qCritical() << "picturesink pad probe is NOT EOS";
    }

    return GST_PAD_PROBE_HANDLED;
}

GstPadProbeReturn gstpipeline::sRawQueueBlockedCallback(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
{
    if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_DATA (info)) == GST_EVENT_EOS)
    {
        gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));

        gst_element_set_state(mPictureFileSink, GST_STATE_NULL);
        gst_element_set_state(mPngEncoder, GST_STATE_NULL);
        gst_element_set_state(mPngVideoConvert, GST_STATE_NULL);
        gst_element_set_state(mPictureQueue, GST_STATE_NULL);

        //unlink the picture pipeline from the src pad of the raw tee and remove that pad
        GstPad* tee_picturesink_pad = gst_element_get_static_pad(mRawTee, "src_1");
        qWarning ("Obtained request pad %s for picturesink branch.", gst_pad_get_name (tee_picturesink_pad));
        GstPad* raw_picture_queue_pad = gst_element_get_static_pad (mPictureQueue, "sink");
        if (gst_pad_unlink (tee_picturesink_pad, raw_picture_queue_pad) != GST_PAD_LINK_OK)
        {
          qCritical ("picture Tee could not be linked.");
        }
        if(gst_element_remove_pad(mRawTee, tee_picturesink_pad) != TRUE)
        {
            qCritical("could not remove raw tee pad");
        }
        g_object_unref(tee_picturesink_pad);
        g_object_unref(raw_picture_queue_pad);

        gst_bin_remove_many(GST_BIN(mPipeline), mPictureQueue, mPngVideoConvert, mPngEncoder, mPictureFileSink, NULL);

        qWarning() << "we have set the fakesink back up";
    }
    else
    {
        qCritical() << "picturesink pad probe is NOT EOS";
    }
    return GST_PAD_PROBE_PASS;
}

gboolean gstpipeline::sMainBusCallback (GstBus*bus, GstMessage *msg, gpointer user_data)
{
  GMainLoop *loop = (GMainLoop*)user_data;

  switch (GST_MESSAGE_TYPE (msg)) {
    case GST_MESSAGE_ERROR:
  {
      GError *err = NULL;
      gchar *dbg;

      gst_message_parse_error (msg, &err, &dbg);
      gst_object_default_error (msg->src, err, dbg);
      g_clear_error (&err);
      g_free (dbg);
      g_main_loop_quit (loop);
  }
      break;
    case GST_MESSAGE_EOS:
      g_print ("we reached EOS\n");
      g_main_loop_quit (loop);
      break;
    default:
//      g_print ("msg: %s\n", GST_MESSAGE_TYPE_NAME(msg));
      break;
  }
}

1 Ответ

1 голос
/ 09 апреля 2020

так что мне удалось понять это самому. вот шаги, которые я предпринял, чтобы заставить это работать:
1. блокирующий зонд в очереди fakesink
2. добавить конвейер изображения
3. поместить блокирующий зонд данных в приемник файлов изображений
4 подождать, пока буфер сегмента не достигнет файлового приемника
5. поместить блокирующий зонд в очередь линий изображения
6. в тесте блокировки очереди, отправить событие eos и удалить конвейер изображения

...