{"id":8790,"date":"2023-09-08T13:12:20","date_gmt":"2023-09-08T12:12:20","guid":{"rendered":"https:\/\/andyland.info\/wordpress\/?p=8790"},"modified":"2023-09-08T13:12:22","modified_gmt":"2023-09-08T12:12:22","slug":"use-ffmpeg-to-find-a-possible-loop-in-a-video","status":"publish","type":"post","link":"https:\/\/andyland.info\/wordpress\/use-ffmpeg-to-find-a-possible-loop-in-a-video\/","title":{"rendered":"Use FFmpeg to find a possible loop in a video"},"content":{"rendered":"\n\n\n<p>As stated an multiple occasions, I am doing live visuals every now and then for various events. Most of my footage is based on <a href=\"https:\/\/learnopengl.com\/Getting-started\/Shaders\">shaders<\/a>. In (very) short: These are programs that are executed on your graphics-card and create visuals in real-time instead of playing pre-made videos. Extremely interesting and extremely nerdy. You might want to check out\u00a0<a href=\"https:\/\/glslsandbox.com\/\">https:\/\/glslsandbox.com\/<\/a> to get an idea. However, trying to run shaders on, for example, a Raspberry Pi 4 instead of a top-tier Macbook pro has a huge amount of its very own quirks. Playing videos on an RPI4, however, doesn&#8217;t cause too many problems.\u00a0<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>So I only need to create a video loop of my shaders and &#8230; done.<\/p>\n<p>Well &#8230;\u00a0 no. The video below is a typical example of a shader that&#8217;s repeating- somewhere. The original video I created out of the shader is 15 seconds in duration. To make things really obvious\u00a0 I created a simple loop with FFmpeg.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ffmpeg -stream_loop 1 -i video.mov -c copy video-loop.mov<\/code><\/pre>\n\n\n\n<p>You&#8217;ll recognize a noticeable cut at ~15 seconds. Obviously, this cannot be it.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"2 loop with seam\" width=\"1333\" height=\"1000\" src=\"https:\/\/www.youtube.com\/embed\/51K1FuKJ9_Q?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>However, using FFmpeg we have the possibility to analyze the difference between frames. That&#8217;s a win. First up we make sure that the original video only consists of keyframes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ffmpeg -i video.mov -g 1 video_all_keyframes.mov<\/code><\/pre>\n\n\n\n<p>Depending on the original material (codec, compression settings, etc.) this might drastically increase the video&#8217;s filesize. However, it&#8217;s only temporary (kind of).<\/p>\n<p>In order to find a (near) seamless loop we now proceed with the new keyframe-only video and compare every frame of the video with a completely black frame. Those frames with a similar difference have a high probability of being visually equal.&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ffmpeg -i video_all_keyframes.mov -filter_complex \"&#91;0]trim=start_frame=0:end_frame=1&#91;c];&#91;c]&#91;0]blend=all_mode=difference,blackframe=99:8\" -f null -<\/code><\/pre>\n\n\n\n<p>(If you copypaste the snippet please pay extra respect to the trailing hyphen). The command&#8217;s output, run in a terminal, looks like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"566\" src=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/Bildschirmfoto-2023-09-08-um-12.45.57-1024x566.png\" alt=\"\" class=\"wp-image-8840\" srcset=\"https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/Bildschirmfoto-2023-09-08-um-12.45.57-1024x566.png 1024w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/Bildschirmfoto-2023-09-08-um-12.45.57-300x166.png 300w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/Bildschirmfoto-2023-09-08-um-12.45.57-768x424.png 768w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/Bildschirmfoto-2023-09-08-um-12.45.57-150x83.png 150w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/Bildschirmfoto-2023-09-08-um-12.45.57-250x138.png 250w, https:\/\/andyland.info\/wordpress\/wp-content\/uploads\/Bildschirmfoto-2023-09-08-um-12.45.57.png 1200w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The interesting parts are the green lines starting with &#8220;[Parsed_blackframe &#8230;&#8221;. The command&#8217;s output lists those frames where a difference to a black frame is less than 8 for 99% of the pixels. All of them are potential candidates for a loop&#8217;s start- and endpoint. Depending on the video (again: codec, compression, etc) you can tweak those values and make the comparison even more or less precise.<\/p>\n<p>For example:<\/p>\n<p>&#8230; <code>all_mode=difference,blackframe=75:50<\/code> &#8230;.<\/p>\n<p>will list all frames where a difference to a black frame is less than 50 for 75% of the pixels. Obviously, the amount of potential frames for a loop will increase but the chance of creating a visually seamless loop will decrease accordingly.<\/p>\n<p>Let&#8217;s try a loop between frame 1 (time: 0.016667) and frame 378 (time: 6.300000 seconds):\u00a0<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ffmpeg -i video_all_keyframes.mov -ss 0.016667 -to 6.300000 potential_loop.mov<\/code><\/pre>\n\n\n\n<p>The resulting video has a duration of 6 seconds. Let&#8217;s loop it 3 times and check if we have a seamless experience:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ffmpeg -stream_loop 3 -i potential_loop.mov -c copy loop.mov<\/code><\/pre>\n\n\n\n<p>The interesting timestamps are (you guessed it) at 6, 12 and 18 seconds. I think it&#8217;s looking very good<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"loop 3\" width=\"1333\" height=\"1000\" src=\"https:\/\/www.youtube.com\/embed\/iEzbsYWMv8I?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>As stated an multiple occasions, I am doing live visuals every now and then for various events. Most of my footage is based on shaders. In (very) short: These are programs that are executed on your graphics-card and create visuals in real-time instead of playing pre-made videos. Extremely interesting and \u2026 <a class=\"continue-reading-link\" href=\"https:\/\/andyland.info\/wordpress\/use-ffmpeg-to-find-a-possible-loop-in-a-video\/\"> Continue reading <span class=\"meta-nav\">&rarr; <\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","footnotes":"","_links_to":"","_links_to_target":""},"categories":[5,21],"tags":[469,473,470,472,62],"class_list":["post-8790","post","type-post","status-publish","format-standard","hentry","category-projekte","category-video","tag-ffmpeg","tag-generic","tag-loop","tag-shader","tag-vdmx","odd"],"_links":{"self":[{"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/posts\/8790","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/comments?post=8790"}],"version-history":[{"count":0,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/posts\/8790\/revisions"}],"wp:attachment":[{"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/media?parent=8790"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/categories?post=8790"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/andyland.info\/wordpress\/wp-json\/wp\/v2\/tags?post=8790"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}