<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:#0563C1;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:#954F72;
        text-decoration:underline;}
span.EpostStil17
        {mso-style-type:personal-compose;
        font-family:"Calibri",sans-serif;
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-family:"Calibri",sans-serif;}
@page WordSection1
        {size:612.0pt 792.0pt;
        margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
        {page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-US" link="#0563C1" vlink="#954F72">
<div class="WordSection1">
<p class="MsoNormal">I am having trouble adding elements dynamically to a tee to allow simultaneous streaming and recording of video when requested by the user.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I first set up the following pipeline to stream video via RTSP:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">nvarguscamerasrc ! video/x-raw(memory:NVMM),width=1280,height=720,framerate=30/1,format=NV12 ! tee name=sourceTee ! queue ! omxh264enc ! rtph264pay name=pay0 pt=96<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I then try to add elements to the sourceTee when the user clicks start and take them down again when the user clicks stop. I try to flush the video capture part of the pipeline by injecting EOS into the video capture queue's sink pad and
 waiting for it to arrive on the filesink.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">It works most of the time, but when I start and stop video capture multiple times, it is not stable, and sometimes it appears that I take down the whole pipeline and the video stream stops.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Can somebody take a look at my code for taking down the pipeline and let me know what I am doing wrong?<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">A struct with elements to capture video to disk:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">struct ArgusVideoCaptureElements<o:p></o:p></p>
<p class="MsoNormal">{<o:p></o:p></p>
<p class="MsoNormal">  GstElement* queue = nullptr;<o:p></o:p></p>
<p class="MsoNormal">  GstElement* nvtee = nullptr;<o:p></o:p></p>
<p class="MsoNormal">  GstElement* convert = nullptr;<o:p></o:p></p>
<p class="MsoNormal">  GstCaps* convertCaps = nullptr;<o:p></o:p></p>
<p class="MsoNormal">  GstElement* encoder = nullptr;<o:p></o:p></p>
<p class="MsoNormal">  GstElement* mux = nullptr;<o:p></o:p></p>
<p class="MsoNormal">  GstElement* filesink = nullptr;<o:p></o:p></p>
<p class="MsoNormal">};<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">This is the code I use to start capturing video (at the same time as the stream is running):<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">void ArgusVideoCapture::start()<o:p></o:p></p>
<p class="MsoNormal">{<o:p></o:p></p>
<p class="MsoNormal">    m_bin = gst_bin_new("video_capture_bin");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    // Pipeline to capture video to disk<o:p></o:p></p>
<p class="MsoNormal">    // sourceTee. ! queue ! nvvidconv ! video/x-raw(memory:NVMM),format=(string)I420 ! omxh264enc ! mp4mux ! filesink location=test.mp4<o:p></o:p></p>
<p class="MsoNormal">    <o:p></o:p></p>
<p class="MsoNormal">    ASSERT_THROW(<o:p></o:p></p>
<p class="MsoNormal">        m_elems.queue = gst_element_factory_make("queue", NULL),
<o:p></o:p></p>
<p class="MsoNormal">        "Video Capture: Could not create queue element");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    ASSERT_THROW(<o:p></o:p></p>
<p class="MsoNormal">        m_elems.nvtee = gst_element_factory_make("nvtee", NULL),
<o:p></o:p></p>
<p class="MsoNormal">        "Video Capture: Could not create nvtee element");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    ASSERT_THROW(<o:p></o:p></p>
<p class="MsoNormal">        m_elems.convert = gst_element_factory_make("nvvidconv", NULL),
<o:p></o:p></p>
<p class="MsoNormal">        "Video Capture: Could not create videoconvert element");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    ASSERT_THROW(<o:p></o:p></p>
<p class="MsoNormal">        m_elems.convertCaps = gst_caps_from_string("video/x-raw(memory:NVMM), format=(string)I420"),<o:p></o:p></p>
<p class="MsoNormal">        "Video Capture: Could not create videoconvert caps");<o:p></o:p></p>
<p class="MsoNormal">        <o:p></o:p></p>
<p class="MsoNormal">    ASSERT_THROW(<o:p></o:p></p>
<p class="MsoNormal">        m_elems.encoder = gst_element_factory_make("omxh264enc", NULL),<o:p></o:p></p>
<p class="MsoNormal">        "Video Capture: Could not create encoder");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    ASSERT_THROW(<o:p></o:p></p>
<p class="MsoNormal">        m_elems.mux = gst_element_factory_make("mp4mux", NULL),<o:p></o:p></p>
<p class="MsoNormal">        "Video Capture: Could not create multiplexer");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    ASSERT_THROW(<o:p></o:p></p>
<p class="MsoNormal">        m_elems.filesink = gst_element_factory_make("filesink", NULL),<o:p></o:p></p>
<p class="MsoNormal">        "Video Capture: Could not create filesink");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    auto filename = generateFilename();<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    gst_util_set_object_arg(G_OBJECT(m_elems.encoder), "tune", "zerolatency");<o:p></o:p></p>
<p class="MsoNormal">    g_object_set(G_OBJECT(m_elems.filesink), "location", filename.c_str(), NULL);<o:p></o:p></p>
<p class="MsoNormal">    g_object_set(G_OBJECT(m_elems.filesink), "sync", FALSE, NULL);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    gst_bin_add_many(GST_BIN(m_bin), m_elems.queue, m_elems.nvtee, m_elems.convert,<o:p></o:p></p>
<p class="MsoNormal">        m_elems.encoder, m_elems.mux, m_elems.filesink, NULL);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    gst_element_link_many(m_elems.queue, m_elems.nvtee, m_elems.convert, NULL);<o:p></o:p></p>
<p class="MsoNormal">    gst_element_link_filtered(m_elems.convert, m_elems.encoder, m_elems.convertCaps);<o:p></o:p></p>
<p class="MsoNormal">    gst_element_link_many(m_elems.encoder, m_elems.mux, m_elems.filesink, NULL);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    // Create ghost pad for bin<o:p></o:p></p>
<p class="MsoNormal">    GstPad* pad = gst_element_get_static_pad(m_elems.queue, "sink");<o:p></o:p></p>
<p class="MsoNormal">    ASSERT_THROW(pad, "Could not get sink pad from queue element");<o:p></o:p></p>
<p class="MsoNormal">    ASSERT_THROW(gst_element_add_pad(m_bin, gst_ghost_pad_new("sink", pad)),<o:p></o:p></p>
<p class="MsoNormal">        "Could not add sink ghost pad to video capture bin");<o:p></o:p></p>
<p class="MsoNormal">    gst_object_unref(GST_OBJECT(pad));<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    GstElement *tee = gst_bin_get_by_name(GST_BIN(m_pipeline), m_teeName.c_str());<o:p></o:p></p>
<p class="MsoNormal">    ASSERT_THROW(tee, "Could not get tee element");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    m_teePad = gst_element_get_request_pad(tee, "src_%u");<o:p></o:p></p>
<p class="MsoNormal">    ASSERT_THROW(m_teePad, "Could not get tee request pad");<o:p></o:p></p>
<p class="MsoNormal">    gst_object_unref(tee);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    ASSERT_THROW(gst_bin_add(GST_BIN(m_pipeline), m_bin), "Could not add video capture bin to pipeline");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    GstPad* sinkPad = gst_element_get_static_pad(m_bin, "sink");<o:p></o:p></p>
<p class="MsoNormal">    ASSERT_THROW(sinkPad, "Could not get sink pad from video capture element");<o:p></o:p></p>
<p class="MsoNormal">    ASSERT_THROW(gst_pad_link(m_teePad, sinkPad) == GstPadLinkReturn::GST_PAD_LINK_OK, "Could not link tee pad to video capture bin");<o:p></o:p></p>
<p class="MsoNormal">    gst_object_unref(sinkPad);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    gst_element_sync_state_with_parent(m_bin);<o:p></o:p></p>
<p class="MsoNormal">}<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">----<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">This is the code I use to take down the video capture bin:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">void ArgusVideoCapture::stop()<o:p></o:p></p>
<p class="MsoNormal">{<o:p></o:p></p>
<p class="MsoNormal">    if (m_teePad == nullptr)<o:p></o:p></p>
<p class="MsoNormal">    {<o:p></o:p></p>
<p class="MsoNormal">        log_warning("Cannot stop video capture since it was not running");<o:p></o:p></p>
<p class="MsoNormal">        return;<o:p></o:p></p>
<p class="MsoNormal">    }<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    m_eosReceived = false;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    gst_pad_add_probe(m_teePad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
<o:p></o:p></p>
<p class="MsoNormal">    [](GstPad* pad, GstPadProbeInfo* info, gpointer user_data) -> GstPadProbeReturn<o:p></o:p></p>
<p class="MsoNormal">        {<o:p></o:p></p>
<p class="MsoNormal">            log_info("Blocking downstream data on the tee pad for video capture");<o:p></o:p></p>
<p class="MsoNormal">            ArgusVideoCapture *self = reinterpret_cast<ArgusVideoCapture *>(user_data);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">            gst_pad_remove_probe(pad, GST_PAD_PROBE_INFO_ID(info));<o:p></o:p></p>
<p class="MsoNormal">            GstPad *sinkPad = gst_element_get_static_pad(self->m_elems.filesink, "sink");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">            // Callback to wait for end of stream<o:p></o:p></p>
<p class="MsoNormal">            gst_pad_add_probe(sinkPad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
<o:p></o:p></p>
<p class="MsoNormal">                [](GstPad* pad, GstPadProbeInfo* info, gpointer userData) -> GstPadProbeReturn<o:p></o:p></p>
<p class="MsoNormal">                {<o:p></o:p></p>
<p class="MsoNormal">                    ArgusVideoCapture *self = reinterpret_cast<ArgusVideoCapture *>(userData);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">                    if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_DATA(info)) != GST_EVENT_EOS)<o:p></o:p></p>
<p class="MsoNormal">                        return GST_PAD_PROBE_PASS;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">                    log_info("File sink received EOS");<o:p></o:p></p>
<p class="MsoNormal">                    gst_pad_remove_probe(pad, GST_PAD_PROBE_INFO_ID(info));<o:p></o:p></p>
<p class="MsoNormal">                    self->m_eosReceived = true;<o:p></o:p></p>
<p class="MsoNormal">                }, self, nullptr);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">            gst_object_unref(sinkPad);<o:p></o:p></p>
<p class="MsoNormal">            GstPad* binSinkPad = gst_element_get_static_pad(self->m_bin, "sink");<o:p></o:p></p>
<p class="MsoNormal">            gst_pad_send_event(binSinkPad, gst_event_new_eos());<o:p></o:p></p>
<p class="MsoNormal">            gst_object_unref(binSinkPad);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">        }, this, nullptr);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    log_info("Unlinking from tee pad");<o:p></o:p></p>
<p class="MsoNormal">    GstPad* sinkPad = gst_element_get_static_pad(m_bin, "sink");<o:p></o:p></p>
<p class="MsoNormal">    gst_pad_unlink(m_teePad, sinkPad);<o:p></o:p></p>
<p class="MsoNormal">    gst_object_unref(sinkPad);<o:p></o:p></p>
<p class="MsoNormal">    gst_object_unref(m_teePad);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    auto start = std::chrono::high_resolution_clock::now();<o:p></o:p></p>
<p class="MsoNormal">    auto now = start;<o:p></o:p></p>
<p class="MsoNormal">    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now-start).count();<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    while (!m_eosReceived && (duration < 3000))<o:p></o:p></p>
<p class="MsoNormal">    {<o:p></o:p></p>
<p class="MsoNormal">        now = std::chrono::high_resolution_clock::now();<o:p></o:p></p>
<p class="MsoNormal">        duration = std::chrono::duration_cast<std::chrono::milliseconds>(now-start).count();<o:p></o:p></p>
<p class="MsoNormal">    }<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    if (!m_eosReceived)<o:p></o:p></p>
<p class="MsoNormal">        log_warning("Timeout while waiting for EOS");<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    gst_pad_add_probe(m_teePad, GST_PAD_PROBE_TYPE_IDLE,<o:p></o:p></p>
<p class="MsoNormal">    [](GstPad* pad, GstPadProbeInfo* info, gpointer userData) -> GstPadProbeReturn<o:p></o:p></p>
<p class="MsoNormal">        {<o:p></o:p></p>
<p class="MsoNormal">            ArgusVideoCapture *self = reinterpret_cast<ArgusVideoCapture*>(userData);<o:p></o:p></p>
<p class="MsoNormal">            log_info("Unlinking from tee pad");<o:p></o:p></p>
<p class="MsoNormal">            GstPad* sinkPad = gst_element_get_static_pad(self->m_bin, "sink");<o:p></o:p></p>
<p class="MsoNormal">            gst_pad_unlink(self->m_teePad, sinkPad);<o:p></o:p></p>
<p class="MsoNormal">            gst_object_unref(sinkPad);<o:p></o:p></p>
<p class="MsoNormal">            gst_object_unref(self->m_teePad);<o:p></o:p></p>
<p class="MsoNormal">            log_info("Setting bin state to NULL");<o:p></o:p></p>
<p class="MsoNormal">            gst_element_set_state(self->m_bin, GST_STATE_NULL);<o:p></o:p></p>
<p class="MsoNormal">            log_info("Removing bin from pipeline");<o:p></o:p></p>
<p class="MsoNormal">            gst_bin_remove(GST_BIN(self->m_pipeline), self->m_bin);<o:p></o:p></p>
<p class="MsoNormal">        }, this, nullptr);    <o:p></o:p></p>
<p class="MsoNormal">}<o:p></o:p></p>
</div>
</body>
</html>