<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Paul – IMG.LY Blog</title><description>I&apos;m a husband, dad, lifelong learner, tech lover, and Senior Engineer working as a Tech Lead. I write about projects and challenges in IT. </description><link>https://img.ly/blog/author/paul/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>Paul – IMG.LY Blog</title><link>https://img.ly/blog/author/paul/</link></image><atom:link href="https://img.ly/blog/author/paul/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Wed, 10 Jun 2026 12:21:44 GMT</lastBuildDate><ttl>60</ttl><item><title>How To Build a Video Player in JavaScript</title><link>https://img.ly/blog/how-to-build-video-player-in-javascript/</link><guid isPermaLink="true">https://img.ly/blog/how-to-build-video-player-in-javascript/</guid><description>Create your own JavaScript video player using simple methods for neat results!</description><pubDate>Tue, 27 Sep 2022 17:02:49 GMT</pubDate><content:encoded>&lt;p&gt;Probably a decade ago, it was impossible to play video or audio inside your browser without any third-party services such as Flash or Silverlight. You needed to install a plugin and only play your media while using it, so as you can see, it was very uncomfortable, with low speed and high delays. Nowadays, we have JavaScript with the new version of HTML5. With these new technologies and tools, we can stream our video much quicker, easier, and without any latency. To do it, you will need only a simple &lt;video&gt; tag and give a link to your video stored on your computer. The simple attribute &lt;em&gt;controls&lt;/em&gt; will give you a default video player built into the browser. It’s elementary and doesn’t have many features, so if you want to stream a video on your website in a more professional way using your video player, you’ll need to use JavaScript. And we’ll teach you how to do it in this article!&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;By the end of this guide, you’ll have something similar to this, so if you’re excited, keep reading and follow this tutorial step-by-step!&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;qBYNxxa&quot; data-user=&quot;paulknulst&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/paulknulst/pen/qBYNxxa&quot;&gt;How to build a video player in Javascript&lt;/a&gt; by Paul Knulst (&lt;a href=&quot;https://codepen.io/paulknulst&quot;&gt;@paulknulst&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;setting-up-the-project&quot;&gt;&lt;strong&gt;Setting Up the Project&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Assuming you are working with UNIX system (or have Git BASH on Windows) you can create all three files that are necessary to build a video player in JavaScript with this command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;mkdir video-player&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd video-player&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;touch index.html script.js style.css&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add a simple video player to our application, we have to add the following code to our &lt;code&gt;index.html&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;!DOCTYPE html&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;html lang=&quot;en&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;head&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;meta charset=&quot;UTF-8&quot; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;title&gt;How to build a video player in Javascript&amp;#x3C;/title&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/head&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;body&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;div class=&quot;player&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &amp;#x3C;video class=&quot;video&quot; controls&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &amp;#x3C;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    src=&quot;&amp;#x3C;https://ftp.f1nalboss.de/data/imgly/videoplayer/testvideo.mp4&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    type=&quot;video/mp4&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &amp;#x3C;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    src=&quot;&amp;#x3C;https://ftp.f1nalboss.de/data/imgly/videoplayer/testvideo.mp4&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    type=&quot;video/webm&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &amp;#x3C;p&gt;No HTML5 video supported&amp;#x3C;/p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &amp;#x3C;/video&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;script src=&quot;script.js&quot;&gt;&amp;#x3C;/script&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/body&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/html&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within the above code, the &lt;code&gt;&amp;#x3C;video&gt;&lt;/code&gt; element uses a remote video from my FTP. You can either use my default video or add any video from your local computer by adjusting the &lt;code&gt;src&lt;/code&gt; attribute. HTML5 specification supports three different video formats, and the snippet used multiple &lt;code&gt;&amp;#x3C;source&gt;&lt;/code&gt; tags to make the videos available in MP4 and WebM. Furthermore, the &lt;code&gt;&amp;#x3C;p&gt;&lt;/code&gt; tag is used to display pre-defined content to user agents that do not support the &lt;code&gt;video&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;The HTML5 &lt;code&gt;&amp;#x3C;video&gt;&lt;/code&gt; tag accepts several native attributes. For example, the &lt;code&gt;controls&lt;/code&gt; attribute displays the standard player controls when added or set to true. You can find out more about &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-controls&quot;&gt;all video attributes here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before continuing, you should apply all styles that are needed within this tutorial by populating your &lt;code&gt;style.css&lt;/code&gt; with all styles &lt;a href=&quot;https://codepen.io/paulknulst/pen/qBYNxxa&quot;&gt;from this CodePen&lt;/a&gt;. Save and open your &lt;code&gt;index.html&lt;/code&gt; and load it within the browser to see the embedded video player as seen below:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;This is what the embedded video player should look like in your index.html file.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 805px) 805px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;805&quot; height=&quot;544&quot; src=&quot;https://img.ly/_astro/build-video-player-with-javascript_ZBugbA.webp&quot; srcset=&quot;/_astro/build-video-player-with-javascript_Z2soYbu.webp 640w, /_astro/build-video-player-with-javascript_ZnTDYb.webp 750w, /_astro/build-video-player-with-javascript_ZBugbA.webp 805w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;customize-the-video-player-with-javascript&quot;&gt;&lt;strong&gt;Customize the Video Player With JavaScript&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;To customize the video player, you first have to remove the &lt;code&gt;controls&lt;/code&gt; attribute that displays &lt;code&gt;Play&lt;/code&gt;, &lt;code&gt;Pause&lt;/code&gt;, &lt;code&gt;Volume&lt;/code&gt;, etc. because you will implement your own custom controls within this tutorial. Now, check your browser, you will recognize that the controls are gone, and you cannot play the video anymore.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 815px) 815px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;815&quot; height=&quot;540&quot; src=&quot;https://img.ly/_astro/build-video-player-with-javascript_2_Z1Exl3w.webp&quot; srcset=&quot;/_astro/build-video-player-with-javascript_2_27jRL3.webp 640w, /_astro/build-video-player-with-javascript_2_Z2tYMIW.webp 750w, /_astro/build-video-player-with-javascript_2_Z1Exl3w.webp 815w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;add-play-and-pause&quot;&gt;&lt;strong&gt;Add Play and Pause&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;To enable play and pause the video, you have to add a new button to the &lt;code&gt;index.html&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;div class=&quot;controls&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;button&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            class=&quot;controls__btn playPauseBtn&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            title=&quot;Toggle Play&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ►&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/button&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterward, open your &lt;code&gt;script.js&lt;/code&gt; and enable functionality by adding this code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const videoContainer = document.querySelector(&quot;.video-container&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const playPauseBtn = document.querySelector(&quot;.playPauseBtn&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function togglePlay() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if (videoContainer.paused || videoContainer.ended) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    videoContainer.play();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } else {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    videoContainer.pause();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function updatePlayBtn() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  playPauseBtn.innerHTML = videoContainer.paused ? &quot;►&quot; : &quot;❚❚&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;playPauseBtn.addEventListener(&quot;click&quot;, togglePlay);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;videoContainer.addEventListener(&quot;click&quot;, togglePlay);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;videoContainer.addEventListener(&quot;play&quot;, updatePlayBtn);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;videoContainer.addEventListener(&quot;pause&quot;, updatePlayBtn);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within this javascript code, first, the &lt;code&gt;video-container&lt;/code&gt; element and the &lt;code&gt;playPauseBtn&lt;/code&gt; is selected (Line 1 and 2). Then two functions are defined: &lt;code&gt;togglePlay()&lt;/code&gt; and &lt;code&gt;updatePlayBtn()&lt;/code&gt;. &lt;code&gt;togglePlay()&lt;/code&gt; is used to stop and start the video based on its actual state. &lt;code&gt;updatePlayBtn&lt;/code&gt; is used to switch between the Icon which is shown within the video player.&lt;/p&gt;
&lt;p&gt;In the last part of the snippet, a click event listener is added to the &lt;code&gt;playPauseBtn&lt;/code&gt; that executes the &lt;code&gt;togglePlay()&lt;/code&gt; function. Next, three click event listeners are added to the &lt;code&gt;videoContainer&lt;/code&gt; that executes &lt;code&gt;togglePlay()&lt;/code&gt; on mouse click and also executes &lt;code&gt;updatePlayBtn&lt;/code&gt; based on the video’s state.&lt;/p&gt;
&lt;p&gt;Now you can reload your &lt;code&gt;index.html&lt;/code&gt; and should be able to play and pause the video by either clicking the video or the button:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;You can now pause and play your video.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 803px) 803px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;803&quot; height=&quot;526&quot; src=&quot;https://img.ly/_astro/javascript-video-player_c0OvI.webp&quot; srcset=&quot;/_astro/javascript-video-player_Z2mALHv.webp 640w, /_astro/javascript-video-player_2aoUuJ.webp 750w, /_astro/javascript-video-player_c0OvI.webp 803w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;add-progress-bar&quot;&gt;Add Progress Bar&lt;/h3&gt;
&lt;p&gt;Next, a progress bar will be implemented to show the current timestamp of the video when played. First, add a &lt;code&gt;div&lt;/code&gt; tag to the &lt;code&gt;index.html&lt;/code&gt; which will act as the progress bar:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;div class=&quot;controls&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;div class=&quot;progress&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    	&amp;#x3C;div class=&quot;progress__filled&quot;&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ..&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then open the &lt;code&gt;script.js&lt;/code&gt; and add the following snippet:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const progress = document.querySelector(&quot;.progress&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const progressBar = document.querySelector(&quot;.progress__filled&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function handleProgress() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const progressPercentage = (videoContainer.currentTime / videoContainer.duration) * 100;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  progressBar.style.flexBasis = `${progressPercentage}%`;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function jump(e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const position = (e.offsetX / progress.offsetWidth) * videoContainer.duration;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoContainer.currentTime = position;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;videoContainer.addEventListener(&quot;timeupdate&quot;, handleProgress);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;progress.addEventListener(&quot;click&quot;, jump);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let mousedown = false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;progress.addEventListener(&quot;mousedown&quot;, () =&gt; (mousedown = true));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;progress.addEventListener(&quot;mousemove&quot;, (e) =&gt; mousedown &amp;#x26;&amp;#x26; jump(e));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;progress.addEventListener(&quot;mouseup&quot;, () =&gt; (mousedown = false));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this snippet, the &lt;code&gt;progress&lt;/code&gt; container and the &lt;code&gt;progress__filled&lt;/code&gt; element will be selected, and two functions will be added: &lt;code&gt;handleProgress()&lt;/code&gt; and &lt;code&gt;jump(e)&lt;/code&gt;. &lt;code&gt;handleProgress()&lt;/code&gt; will be responsible for updating the progress bar. The &lt;code&gt;jump(e)&lt;/code&gt; function is used to enable clicking on the progress bar to jump to the position within the video.&lt;/p&gt;
&lt;p&gt;The last part contains all event listeners that are needed for the progress bar. The &lt;code&gt;handleProgress()&lt;/code&gt; will be called on every &lt;code&gt;timeupdate&lt;/code&gt; event. Also, clicking anywhere on the progress bar will call the &lt;code&gt;jump(e)&lt;/code&gt; method and the video will jump to the position. Additionally, &lt;code&gt;mousedown&lt;/code&gt;, &lt;code&gt;mousemove&lt;/code&gt;, and &lt;code&gt;mouseup&lt;/code&gt; will be used to enable &lt;em&gt;sliding&lt;/em&gt; through the video while holding the mouse button down on the progress bar.&lt;/p&gt;
&lt;h2 id=&quot;closing-notes&quot;&gt;&lt;strong&gt;Closing Notes&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Congratulations! If you followed the tutorial, you learned how to implement your own video player and add custom controls using JavaScript. Now, you can use &lt;a href=&quot;https://codepen.io/paulknulst/pen/qBYNxxa&quot;&gt;my CodePen&lt;/a&gt; and start implementing more controls like &lt;strong&gt;volume control&lt;/strong&gt;, &lt;strong&gt;keyboard shortcuts&lt;/strong&gt;, or &lt;strong&gt;skip controls&lt;/strong&gt; to build your own customized video player.&lt;/p&gt;
&lt;p&gt;If your app goes beyond merely displaying video, and you want to allow your users to also edit video or create video based templates in the browser, check out our &lt;a href=&quot;https://img.ly/use-cases/video-for-web&quot;&gt;Video Editor for Web&lt;/a&gt;!&lt;/p&gt;</content:encoded><dc:creator>Paul</dc:creator><media:content url="https://blog.img.ly/2022/09/video-player-javascript_tutorial.png" medium="image"/><category>How-To</category><category>Video Editor</category><category>Video Player</category><category>JavaScript</category><category>Web Development</category><category>Web Application</category><category>Tutorial</category></item><item><title>How To Crop and Trim Videos In Kotlin for Android</title><link>https://img.ly/blog/how-to-crop-and-trim-videos-in-kotlin-for-android/</link><guid isPermaLink="true">https://img.ly/blog/how-to-crop-and-trim-videos-in-kotlin-for-android/</guid><description>In this beginner-friendly tutorial you will learn how to crop and trim videos in Android with FFmpeg.</description><pubDate>Fri, 02 Sep 2022 10:18:21 GMT</pubDate><content:encoded>&lt;p&gt;Cropping and trimming videos is a notoriously difficult task to achieve on Android. One way to implement this functionality is by using FFmpeg a free open-source suite of tools that can perform a wide range of tasks, from video converting to editing. Normally FFmpeg is used from the command line, to use it correctly in Android you have to understand its underlying APIs and how to use them.&lt;/p&gt;
&lt;p&gt;In this tutorial, you will learn how to crop and trim videos in Android by using FFmpeg. Even if you are a beginner you should be able to follow the steps to achieve the desired results.&lt;/p&gt;
&lt;p&gt;I try to summarise the most important basics that you need to know to manipulate videos with FFmpeg. After reading this article you should be able to use it in your own applications. Furthermore, I have developed a sample application and library that can be used to trim and crop videos with an Android device.&lt;/p&gt;
&lt;h2 id=&quot;what-is-ffmpeg&quot;&gt;What is FFmpeg&lt;/h2&gt;
&lt;p&gt;FFmpeg is a great multimedia framework that is able to &lt;strong&gt;mux&lt;/strong&gt;, &lt;strong&gt;demux&lt;/strong&gt;, &lt;strong&gt;decode&lt;/strong&gt;, &lt;strong&gt;encode&lt;/strong&gt;, &lt;strong&gt;transcode&lt;/strong&gt;, &lt;strong&gt;filter&lt;/strong&gt;, &lt;strong&gt;stream&lt;/strong&gt;, &lt;strong&gt;and&lt;/strong&gt; &lt;strong&gt;play&lt;/strong&gt; most media content that exists. FFmpeg supports different tools that can be used to develop an application to manipulate any kind of media to the desired output. You do not have any limitations on what to do with multimedia when using FFmpeg.&lt;/p&gt;
&lt;p&gt;Unfortunately, to use FFmpeg in an Android App using Kotlin you have to compile and build the libraries to use them as a dependency in your project. This process is not straightforward because you have to manually compile a C/C++ library with help of the Android NDK.&lt;/p&gt;
&lt;p&gt;Luckily, many people already have done this and we are able to use their compiled library in our project. However, if you want to compile FFmpeg from scratch you can do this. All necessary information should be available within the &lt;a href=&quot;https://developer.android.com/studio/projects/add-native-code&quot;&gt;android developer documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To show you how we can use FFmpeg in our app I will use a compiled FFmpeg library that can be found &lt;a href=&quot;https://github.com/WritingMinds/ffmpeg-android-java&quot;&gt;here&lt;/a&gt;. To find other FFmpeg libraries you can have a look at the &lt;a href=&quot;https://trac.ffmpeg.org/wiki/CompilationGuide/Android&quot;&gt;official FFmpeg wiki&lt;/a&gt; where several pre-packaged sources are listed.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-the-project&quot;&gt;Setting up the project&lt;/h2&gt;
&lt;p&gt;In order to use the FFmpeg library of WritingMinds (or any other library), we have to follow some simple steps.&lt;/p&gt;
&lt;h3 id=&quot;1-add-dependency-for-the-ffmpeg-library-to-your-app-level-buildgradle&quot;&gt;1. Add dependency for the FFmpeg library to your app-level build.gradle&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    implementation fileTree(dir: &apos;libs&apos;, include: [&apos;*.jar&apos;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    implementation &apos;com.writingminds:FFmpegAndroid:0.3.2&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-check-if-your-device-supports-the-current-implementation-of-ffmpeg-or-not&quot;&gt;2. Check if your device supports the current implementation of FFmpeg or not&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; initialize&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; ffmpeg &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; FFmpeg.&lt;/span&gt;&lt;span&gt;getInstance&lt;/span&gt;&lt;span&gt;(ctx.applicationContext)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            ffmpeg.&lt;/span&gt;&lt;span&gt;loadBinary&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;LoadBinaryResponseHandler&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFinish&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onFinish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onSuccess&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onSuccess&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFailure&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onFailure&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onStart&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onStart&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (e: &lt;/span&gt;&lt;span&gt;FFmpegNotSupportedException&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;Your device does not support FFmpeg&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-initialize-the-ffmpeg-module-leave-command-blank&quot;&gt;3. Initialize the FFmpeg module (leave command blank)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; ffmpeg &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; FFmpeg.&lt;/span&gt;&lt;span&gt;getInstance&lt;/span&gt;&lt;span&gt;(ctx)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ffmpeg.&lt;/span&gt;&lt;span&gt;loadBinary&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;FFmpegLoadBinaryResponseHandler&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFinish&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;onFinish&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onSuccess&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;onSuccess&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; //TODO: the command will added here later&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    ffmpeg.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(command, &lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;ExecuteBinaryResponseHandler&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onSuccess&lt;/span&gt;&lt;span&gt;(message: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onSuccess&lt;/span&gt;&lt;span&gt;(message)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onSuccess: &quot;&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; message&lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onProgress&lt;/span&gt;&lt;span&gt;(message: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onProgress&lt;/span&gt;&lt;span&gt;(message)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onProgress: &quot;&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; message&lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFailure&lt;/span&gt;&lt;span&gt;(message: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onFailure&lt;/span&gt;&lt;span&gt;(message)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onFailure: &quot;&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; message&lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onStart&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onStart&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onStart&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFinish&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onFinish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onFinish&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (e: &lt;/span&gt;&lt;span&gt;FFmpegCommandAlreadyRunningException&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;FFmpeg runs already&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFailure&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;onFailure&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onStart&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;4-implement-the-commands-you-want-to-use-in-your-app&quot;&gt;4. Implement the commands you want to use in your app&lt;/h3&gt;
&lt;p&gt;All commands that are supported by FFmpeg can be included in your app with help of an array. This is done by passing every command line argument as a single element within the array. The array will then be translated into an FFmpeg command using the execute method from FFmpeg:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ffmpeg.execute(command, object : ExecuteBinaryResponseHandler() { ... }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;As we want to implement Trim and Crop I will show how this can be done using the &lt;code&gt;arrayOf&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trim:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-y&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-ss&quot;&lt;/span&gt;&lt;span&gt;, startPos, &lt;/span&gt;&lt;span&gt;&quot;-to&quot;&lt;/span&gt;&lt;span&gt;, endPos, &lt;/span&gt;&lt;span&gt;&quot;-c&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;copy&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;“-y”:&lt;/strong&gt; overwrites output files without asking&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-i”:&lt;/strong&gt; specifies an input file&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;input:&lt;/strong&gt; the path of the source video to trim&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-ss”:&lt;/strong&gt; specifies that the next value will be the starting point of the resulting video&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;startPos:&lt;/strong&gt; the starting position in “%d:%02d:%02d” format&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-to”:&lt;/strong&gt; specifies that the next value will be the end position of the resulting video&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;endPos:&lt;/strong&gt; the end position in “%d:%02d:%02d” format&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-c” AND “copy”&lt;/strong&gt;: defines that the stream will not be encoded. The resulting video will only be saved.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;output:&lt;/strong&gt; the path of the resulting video&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To better understand every argument you can have a look at &lt;a href=&quot;https://www.ffmpeg.org/ffmpeg.html#Main-options&quot;&gt;the official FFmpeg documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, to provide arguments for &lt;code&gt;startPos&lt;/code&gt; and &lt;code&gt;endPos&lt;/code&gt; you normally would have to use a utility function that converts the timestamp of the start and end position into the desired “%d:%02d:%02d” format (a string):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; convertTimestampToString&lt;/span&gt;&lt;span&gt;(timeInMs: &lt;/span&gt;&lt;span&gt;Float&lt;/span&gt;&lt;span&gt;): &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; totalSeconds &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (timeInMs &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; 1000&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;toInt&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; seconds &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; totalSeconds &lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; minutes &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; totalSeconds &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;span&gt; %&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; hours &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; totalSeconds &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; 3600&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; formatter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Formatter&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (hours &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            formatter.&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;%d:%02d:%02d&quot;&lt;/span&gt;&lt;span&gt;, hours, minutes, seconds).&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            formatter.&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;%02d:%02d&quot;&lt;/span&gt;&lt;span&gt;, minutes, seconds).&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Crop:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-filter:v&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;crop=&lt;/span&gt;&lt;span&gt;$w&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;$h&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;$x&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;$y&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-threads&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;5&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-preset&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;ultrafast&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-strict&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-2&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-c:a&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;copy&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;“-i”:&lt;/strong&gt; specifies an input file&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;input:&lt;/strong&gt; the path of the source video to trim&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-filter:v”:&lt;/strong&gt; defines that a filtergraph is used&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“crop=$w:$h:$x:$y”:&lt;/strong&gt; use crop functionality to crop a part from the video start at point x:y and having a width(w) and height(h).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-threads”:&lt;/strong&gt; specifies that the next value will set the thread count&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“5”:&lt;/strong&gt; the number of threads to use&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-preset”:&lt;/strong&gt; specifies that the next value will set the encoding preset&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“ultrafast”:&lt;/strong&gt; the encoding preset to use&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-strict”:&lt;/strong&gt; specifies how strictly the standards should be followed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-2”:&lt;/strong&gt; the strict value&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-c:a” AND “copy”&lt;/strong&gt; defines that the stream will not be encoded and ALL audio streams will also be used.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;output:&lt;/strong&gt; the path of the resulting video&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, check &lt;a href=&quot;https://www.ffmpeg.org/ffmpeg.html#Main-options&quot;&gt;the official FFmpeg documentation&lt;/a&gt; to better understand every argument.&lt;/p&gt;
&lt;h3 id=&quot;5-implement-the-ui&quot;&gt;5. Implement the UI&lt;/h3&gt;
&lt;p&gt;To create a fancy UI you should create a custom view that shows some of the frames available in the video (&lt;code&gt;VideoPreviewView&lt;/code&gt;). Also, it should contain a &lt;code&gt;SeekBar&lt;/code&gt; for &lt;strong&gt;cropping&lt;/strong&gt; and a &lt;code&gt;RangeSeekBar&lt;/code&gt; for &lt;strong&gt;trimming&lt;/strong&gt;. The &lt;code&gt;SeekBar&lt;/code&gt; will be used to switch to a certain timestamp within the video to see what is actually trimmed or cropped. The RangeSeekBar will only be used within the trimming UI to define the start and end position of the resulting video.&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;SeekBar&lt;/code&gt; is &lt;a href=&quot;https://developer.android.com/reference/android/widget/SeekBar&quot;&gt;an Android Widget&lt;/a&gt; the &lt;code&gt;RangeSeekBar&lt;/code&gt; is more complicated but there exist several implementations that can be used: By &lt;a href=&quot;https://github.com/tizisdeepan/VideoEditor/blob/master/video-editor/src/main/java/com/video/trimmer/view/RangeSeekBarView.kt&quot;&gt;Tiszideepan&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can implement the &lt;code&gt;VideoPreviewView&lt;/code&gt; with an easy approach. Divide the video into a specific number of frames that are based on the view’s width and display every frame sequentially within a single view to have a preview. To do this you need the width of the view and the duration of the video that can be found using the &lt;a href=&quot;https://developer.android.com/reference/android/media/MediaMetadataRetriever&quot;&gt;MediaMetadataRetriever&lt;/a&gt;. With the &lt;code&gt;MediaMetadataRetriever&lt;/code&gt;, you can use &lt;code&gt;getFrameAtTime()&lt;/code&gt; to fetch a single frame at a specific timestamp. If you now want to display a complete video preview you need to display &lt;code&gt;viewWidth / frameWidth&lt;/code&gt; frames. Unfortunately, depending on the video length and video width, it could happen that only a few frames will be present within the &lt;code&gt;VideoPreviewView&lt;/code&gt;. To fix this problem you have to maintain a threshold to ensure that a specific number of frames are displayed. This means that you have to crop the frames to a certain width until calculated frames equal the threshold.&lt;/p&gt;
&lt;p&gt;The following code snippet will show how you can achieve this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; createPreview&lt;/span&gt;&lt;span&gt;(viewWidth: &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BackgroundExecutor.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BackgroundExecutor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0L&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; execute&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; threshold &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 11&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; thumbnails &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; LongSparseArray&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Bitmap&lt;/span&gt;&lt;span&gt;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; mediaMetadataRetriever &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; MediaMetadataRetriever&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;setDataSource&lt;/span&gt;&lt;span&gt;(context, videoUri)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; videoLengthInMs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (Integer.&lt;/span&gt;&lt;span&gt;parseInt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;extractMetadata&lt;/span&gt;&lt;span&gt;(MediaMetadataRetriever.METADATA_KEY_DURATION)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    ) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1000&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;toLong&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; frameHeight &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; viewHeight&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; initialBitmap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;getFrameAtTime&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        MediaMetadataRetriever.OPTION_CLOSEST_SYNC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; frameWidth &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        ((initialBitmap.width.&lt;/span&gt;&lt;span&gt;toFloat&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; initialBitmap.height.&lt;/span&gt;&lt;span&gt;toFloat&lt;/span&gt;&lt;span&gt;()) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; frameHeight.&lt;/span&gt;&lt;span&gt;toFloat&lt;/span&gt;&lt;span&gt;()).&lt;/span&gt;&lt;span&gt;toInt&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    var&lt;/span&gt;&lt;span&gt; numThumbs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ceil&lt;/span&gt;&lt;span&gt;((viewWidth.&lt;/span&gt;&lt;span&gt;toFloat&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; frameWidth)).&lt;/span&gt;&lt;span&gt;toInt&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    if&lt;/span&gt;&lt;span&gt; (numThumbs &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; threshold) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        numThumbs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; threshold&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; cropWidth &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; viewWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; threshold&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; interval &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; videoLengthInMs &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; numThumbs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    for&lt;/span&gt;&lt;span&gt; (i &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; until numThumbs) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        var&lt;/span&gt;&lt;span&gt; bitmap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;getFrameAtTime&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            i &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; interval,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            MediaMetadataRetriever.OPTION_CLOSEST_SYNC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        bitmap?.&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                bitmap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Bitmap.&lt;/span&gt;&lt;span&gt;createScaledBitmap&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                    bitmap,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                    frameWidth,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                    frameHeight,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                    false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                bitmap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Bitmap.&lt;/span&gt;&lt;span&gt;createBitmap&lt;/span&gt;&lt;span&gt;(bitmap, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, cropWidth, bitmap.height)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (e: &lt;/span&gt;&lt;span&gt;Exception&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;error while create bitmap: &lt;/span&gt;&lt;span&gt;$e&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            thumbnails.&lt;/span&gt;&lt;span&gt;put&lt;/span&gt;&lt;span&gt;(i.&lt;/span&gt;&lt;span&gt;toLong&lt;/span&gt;&lt;span&gt;(), bitmap)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    returnBitmaps&lt;/span&gt;&lt;span&gt;(thumbnails)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (e: &lt;/span&gt;&lt;span&gt;Throwable&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    Thread.&lt;/span&gt;&lt;span&gt;getDefaultUncaughtExceptionHandler&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        .&lt;/span&gt;&lt;span&gt;uncaughtException&lt;/span&gt;&lt;span&gt;(Thread.&lt;/span&gt;&lt;span&gt;currentThread&lt;/span&gt;&lt;span&gt;(), e)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additionally, for cropping a video you need to display a &lt;em&gt;crop rectangle&lt;/em&gt; within the UI to let users decide which part of the video they want to crop. Also, you have to give users the ability to reposition the crop area. To do this, you can use &lt;a href=&quot;https://github.com/ArthurHub/Android-Image-Cropper&quot;&gt;the ImageCropper library from ArthurHub&lt;/a&gt; which is initially designed to crop an image but can also be used for videos as you only need the values from the rectangle. Add the &lt;code&gt;ImageCropper&lt;/code&gt; to your project:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    implementation &apos;com.theartofdev.edmodo:android-image-cropper:2.8.0&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, you can extend a &lt;code&gt;CropperActivity&lt;/code&gt; which loads the current frame of the video (that you can extract with the &lt;code&gt;VideoPreviewView&lt;/code&gt;) by inserting the &lt;code&gt;CropImageView&lt;/code&gt; layout to get the cropping bounds. Once, you select an area in the UI, the rectangle values can be extracted and used to calculate the needed values for video cropping with this function:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; rect &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; cropFrame.cropRect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; abs&lt;/span&gt;&lt;span&gt;(rect.left &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; rect.right)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; h &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; abs&lt;/span&gt;&lt;span&gt;(rect.top &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; rect.bottom)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; rect.left&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; rect.top&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These values will then be used as input variables for the FFmpeg crop command.&lt;/p&gt;
&lt;p&gt;To help you use all this information I created a project that uses all explained snippets to create a sample app that can crop and trim videos. The source code can be found &lt;a href=&quot;https://github.com/paulknulst/VideoTrimmer&quot;&gt;on my personal GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;fixing-error13-permission-denied&quot;&gt;Fixing error=13, Permission denied&lt;/h2&gt;
&lt;p&gt;Unfortunately, if you implement the described functionality and use the compiled FFmpeg library you will run into this problem if you compile your app for SDK 29 and onwards.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.knulst.de/content/images/2022/08/image-4.png&quot; alt=&quot;Permission denied error due to wrong target SDK&quot;&gt;&lt;/p&gt;
&lt;p&gt;Permission denied error due to wrong target SDK The problem is caused because from Android Q onwards it is not allowed to execute binaries in your app’s private data directory. The error is described in &lt;a href=&quot;https://issuetracker.google.com/issues/128554619?pli=1&quot;&gt;the official issue tracker from google&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One solution will be that you compile your app for SDK 28 but this could lead to problems if you try to put your app into the Android Play Store.&lt;/p&gt;
&lt;p&gt;Another solution will be that you compile your app for SDK 29 and stop putting binaries in any not supported directory. Unfortunately, the code that moves the binaries is within the 3rd party lib from WritingMinds and has to be removed by you (with a Pull Request) or by the maintainer of the library. A future-proof solution will be that you stop using external binaries and start compiling dependencies as an NDK project. This will be a lot of work but you can find help within a famous repository that compiles CPP to Java and also has &lt;a href=&quot;https://github.com/bytedeco/javacpp-presets/tree/master/ffmpeg&quot;&gt;FFmpeg&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;add-more-commands&quot;&gt;Add more commands&lt;/h2&gt;
&lt;p&gt;After you implemented crop and trim you maybe want to add more cool features to your Android app. To do this with FFmpeg and the previously described code you can do this by simply creating a new function within the &lt;code&gt;VideoCommands()&lt;/code&gt; and translate the FFmpeg command into a &lt;code&gt;VideoCommands()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;The next sections will describe some fancy functions that would improve the usability of your app. Just add a new function with this command and fill the callbacks with a toast or anything else. Then call the function from anywhere within your app (because you do not need UI for this).&lt;/p&gt;
&lt;h3 id=&quot;removing-audio-from-videos&quot;&gt;Removing audio from videos&lt;/h3&gt;
&lt;p&gt;You might run into a scenario where you’d only like to keep the visuals of a video and remove the audio track, for instance for voicing over certain footage or removing background noise:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-an&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;compress-a-video&quot;&gt;Compress a video&lt;/h3&gt;
&lt;p&gt;Make big videos smaller to save your valuable disk resources.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-vf&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;scale=&lt;/span&gt;&lt;span&gt;$w&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;$h&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-c:v&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;libx264&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-preset&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;veryslow&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-crf&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;24&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;$w and $h&lt;/strong&gt; are the new sizes (should be smaller). If one is -1 it will keep the ratio and is automatically adjusted in relation to the other one.&lt;/p&gt;
&lt;h3 id=&quot;change-playback-speed&quot;&gt;Change playback speed&lt;/h3&gt;
&lt;p&gt;Increase the playback speed to make long videos faster or decrease it for showing something really awesome very slowly.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-vf&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;setpts=&lt;/span&gt;&lt;span&gt;$scale&lt;/span&gt;&lt;span&gt;*PTS&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;$scale&lt;/strong&gt; should be &gt; 1 for slowing down a video or &amp;#x3C; 1 for fastening a video.&lt;/p&gt;
&lt;h3 id=&quot;more&quot;&gt;More&lt;/h3&gt;
&lt;p&gt;To find more cool commands search &lt;a href=&quot;https://www.ffmpeg.org/ffmpeg.html&quot;&gt;the official argument documentation&lt;/a&gt; and implement them by putting every argument into a value of the array function as described earlier.&lt;/p&gt;
&lt;p&gt;For production-grade application you may be better served by using our &lt;a href=&quot;https://img.ly/products/video-sdk&quot;&gt;VideoEditorSDK&lt;/a&gt; which in addition to professional trim and crop features allows your users to add filters, text and audio overlays. Recently, we released time-based sprites as well allowing users to time the appearance of text and stickers in videos.&lt;/p&gt;
&lt;h3 id=&quot;closing-notes&quot;&gt;Closing Notes&lt;/h3&gt;
&lt;p&gt;In this article, you learned that you can use FFmpeg to crop and trim videos on Android. Unfortunately, it does not work out of the box and you have to compile the library using complex techniques. Luckily, there exist some useful libraries that do the heavy lifting for you.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/paulknulst/VideoTrimmer&quot;&gt;Using my app as a baseline&lt;/a&gt; you are able to crop and trim videos. Additionally, you can implement more commands easily if they do not need a GUI because creating a fancy-looking GUI was a complicated part of developing a cropping and trimming app.&lt;/p&gt;
&lt;p&gt;Unfortunately, there are problems with the library that was used because Android Q introduces a security fix that forbids apps to execute binaries within their data folder. But, this is only a problem if you want to create a “Google Play Store” ready app because you can avoid this problem if compile your app for SDK 28 (and lower). Keep in mind that if you want to create a private app that you distribute on your website instead of using the Play Store you can do this. But, if you want to upload your app to the Google Play Store you have to search for &lt;a href=&quot;https://trac.ffmpeg.org/wiki/CompilationGuide/Android&quot;&gt;another FFmpeg library within the documentation&lt;/a&gt; or build one from scratch so you can compile your app for the current needed SDK.&lt;/p&gt;
&lt;p&gt;Looking to integrate video capabilities into your app? Check out our &lt;a href=&quot;https://img.ly/products/video-sdk&quot;&gt;Video Editor SDK&lt;/a&gt;, &lt;a href=&quot;https://img.ly/use-cases/story-reels-short-video-creation&quot;&gt;Short Video Creation&lt;/a&gt;, and &lt;a href=&quot;https://img.ly/products/camera-sdk&quot;&gt;Camera SDK&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I hope you enjoyed reading this article and are able to create an Android app that can crop and trim videos. If you have any questions, need help, or want to give feedback @ us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt;. We are happy to help.&lt;/p&gt;</content:encoded><dc:creator>Paul</dc:creator><media:content url="https://blog.img.ly/2022/09/Android-trimmimg-with-Kotlin.png" medium="image"/><category>How-To</category><category>Tech</category><category>Android</category><category>Android App Development</category><category>Tutorial</category></item></channel></rss>