<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>