{"id":215,"date":"2014-08-27T10:14:49","date_gmt":"2014-08-27T08:14:49","guid":{"rendered":"https:\/\/coaxion.net\/blog\/?p=215"},"modified":"2014-08-27T20:54:46","modified_gmt":"2014-08-27T18:54:46","slug":"concatenate-multiple-streams-gaplessly-with-gstreamer","status":"publish","type":"post","link":"https:\/\/coaxion.net\/blog\/2014\/08\/concatenate-multiple-streams-gaplessly-with-gstreamer\/","title":{"rendered":"Concatenate multiple streams gaplessly with GStreamer"},"content":{"rendered":"<p>Earlier this month I wrote a new <a href=\"http:\/\/gstreamer.freedesktop.org\/\" title=\"GStreamer\" target=\"_blank\">GStreamer<\/a> element that is now integrated into core and will be part of the 1.6 release. It solves yet another commonly asked question on the mailing lists and IRC: How to concatenate multiple streams without gaps between them as if they were a single stream. This is solved by the <a href=\"http:\/\/cgit.freedesktop.org\/gstreamer\/gstreamer\/tree\/plugins\/elements\/gstconcat.c\" title=\"concat\" target=\"_blank\">concat<\/a> element now.<\/p>\n<p>Here are some examples about how it can be used:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sh\" data-enlighter-title=\"\"># 100 frames of the SMPTE test pattern, then the ball pattern\r\ngst-launch-1.0 concat name=c ! videoconvert ! videoscale ! autovideosink  videotestsrc num-buffers=100 ! c.   videotestsrc num-buffers=100 pattern=ball ! c.\r\n\r\n# Basically: $ cat file1 file2 &gt; both\r\ngst-launch-1.0 concat name=c ! filesink location=both   filesrc location=file1 ! c.   filesrc location=file2 ! c.\r\n\r\n# Demuxing two MP4 files with h264 and passing them through the same decoder instance\r\n# Note: this works better if both streams have the same h264 configuration\r\ngst-launch-1.0 concat name=c ! queue ! avdec_h264 ! queue ! videoconvert ! videoscale ! autovideosink   filesrc location=1.mp4 ! qtdemux ! h264parse ! c.   filesrc location=2.mp4 ! qtdemux ! h264parse ! c.<\/pre>\n<p>If you run this in an application that also reports time and duration you will see that concat preserves the stream time, i.e. the position reporting goes back to 0 when switching to the next stream and the duration is always the one of the current stream. However the running time will be continuously increasing from stream to stream.<\/p>\n<p>Also as you can notice, this only works for a single stream (i.e. one video stream or one audio stream, not a container stream with audio and video). To gaplessly concatenate multiple streams that contain multiple streams (e.g. one audio and one video track) one after another a more complex pipeline involving multiple concat elements and the <a href=\"http:\/\/gstreamer.freedesktop.org\/data\/doc\/gstreamer\/head\/gst-plugins-base-plugins\/html\/gst-plugins-base-plugins-streamsynchronizer.html\" title=\"streamsynchronizer\" target=\"_blank\">streamsynchronizer<\/a> element will be necessary to keep everything synchronized.<\/p>\n<h4>Details<\/h4>\n<p>The concat element has <a href=\"http:\/\/gstreamer.freedesktop.org\/data\/doc\/gstreamer\/head\/gstreamer\/html\/GstElement.html#gst-element-request-pad\" title=\"gst_element_request_pad()\" target=\"_blank\">request sinkpads<\/a>, and it concatenates streams in the order in which those sinkpads were requested. All streams except for the currently playing one are blocked until the currently playing one sends an EOS event, and then the next stream will be unblocked. You can request and <a href=\"http:\/\/gstreamer.freedesktop.org\/data\/doc\/gstreamer\/head\/gstreamer\/html\/GstElement.html#gst-element-release-request-pad\" title=\"gst_element_release_request_pad()\" target=\"_blank\">release sinkpads<\/a> at any time, and releasing the currently playing sinkpad will cause concat to switch to the next one immediately.<\/p>\n<p>Currently concat only works with segments in <a href=\"http:\/\/gstreamer.freedesktop.org\/data\/doc\/gstreamer\/head\/gstreamer\/html\/gstreamer-GstFormat.html#GstFormat\" title=\"GstFormat\" target=\"_blank\">GST_FORMAT_TIME and GST_FORMAT_BYTES format<\/a>, and requires all streams to have the same segment format.<\/p>\n<p>From an application side you could implement the same behaviour as concat implements by using pad probes (waiting for EOS) and using pad offsets (gst_pad_set_offset()) to adjust the running times. But by using the concat element this should be a lot easier to implement.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Earlier this month I wrote a new GStreamer element that is now integrated into core and will be part of the 1.6 release. It solves yet another commonly asked question on the mailing lists and IRC: How to concatenate multiple streams without gaps between them as if they were a single stream. This is solved &hellip; <a href=\"https:\/\/coaxion.net\/blog\/2014\/08\/concatenate-multiple-streams-gaplessly-with-gstreamer\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Concatenate multiple streams gaplessly with GStreamer<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[3,5],"tags":[],"class_list":["post-215","post","type-post","status-publish","format-standard","hentry","category-free-software","category-gstreamer"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/posts\/215","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/comments?post=215"}],"version-history":[{"count":10,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/posts\/215\/revisions"}],"predecessor-version":[{"id":228,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/posts\/215\/revisions\/228"}],"wp:attachment":[{"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/media?parent=215"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/categories?post=215"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coaxion.net\/blog\/wp-json\/wp\/v2\/tags?post=215"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}