Skip to content
geeksforgeeks
  • Tutorials
    • Python
    • Java
    • Data Structures & Algorithms
    • ML & Data Science
    • Interview Corner
    • Programming Languages
    • Web Development
    • CS Subjects
    • DevOps And Linux
    • School Learning
    • Practice Coding Problems
  • Courses
    • DSA to Development
    • Get IBM Certification
    • Newly Launched!
      • Master Django Framework
      • Become AWS Certified
    • For Working Professionals
      • Interview 101: DSA & System Design
      • Data Science Training Program
      • JAVA Backend Development (Live)
      • DevOps Engineering (LIVE)
      • Data Structures & Algorithms in Python
    • For Students
      • Placement Preparation Course
      • Data Science (Live)
      • Data Structure & Algorithm-Self Paced (C++/JAVA)
      • Master Competitive Programming (Live)
      • Full Stack Development with React & Node JS (Live)
    • Full Stack Development
    • Data Science Program
    • All Courses
  • Java Arrays
  • Java Strings
  • Java OOPs
  • Java Collection
  • Java 8 Tutorial
  • Java Multithreading
  • Java Exception Handling
  • Java Programs
  • Java Project
  • Java Collections Interview
  • Java Interview Questions
  • Java MCQs
  • Spring
  • Spring MVC
  • Spring Boot
  • Hibernate
Open In App
Next Article:
How to Use FFmpeg in Android with Example?
Next article icon

How to Use FFmpeg in Android with Example?

Last Updated : 08 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

FFmpeg, short for Fast-forward MPEG, is a free and open-source multimedia framework, which is able to decode, encode, transcode, mux, demux, stream, filter and play fairly all kinds of multimedia files that have been created to date. It also supports some of the eldest formats. FFmpeg compiles and runs, across various operating systems like Linux, Mac OS X, Microsoft Windows, the BSDs, Solaris, etc. among a large range of build environments, machine architectures, and configurations. Programming languages used in FFmpeg are C and Assembly language. We can do many fun kinds of stuff using Ffmpeg like, Video Compress, Audio Compress, Trim Video, Rotate Video, Crop Video, Adding filters to videos, Reverse a Video, Creating fast and slow-motion video, Fade in fade out, Merge audio and video, Creating a video from images, Convert video from one format into another, Extract Picture from Video or Sound from Video, Gifs overlay, and many more. FFmpeg is part of the workflow of hundreds of other media-related software projects, and it's often used behind the scenes. Also, It is an internal part of software such as VLC media player, YouTube, Plex, iTunes, Shortcut, Blender, Kodi, HandBrake, it handles video and audio playback in Google Chrome, and Linux version of Firefox. FFmpeg comprises an enormous set-up of libraries and projects for dealing with video, sound, and other multimedia files and streams.

FFmpeg Libraries 

  • libavutil is a utility library to help versatile media programming. It contains portable string functions, arbitrary number generators, extra arithmetic capacities, data structures, cryptography, and core multimedia utilities.
  • libavcodec is a library that provides encoders and decoders for video/audio codecs, subtitle streams, and several bitstream channels.
  • libavformat is a library that provides multiplexing and demultiplexing framework for video/audio codecs, subtitle streams
  • libavdevice is a library containing I/O devices for getting from and delivering to numerous multimedia I/O programming systems, including Video4Linux, ALSA, and VfW.
  • libavfilter library provides a media filtering framework that contains several filters and sinks.
  • libswscale library performs exceptionally enhanced picture scaling and pixel format transformation tasks.
  • libswresample is a library that performs highly optimized but a lossy change in audio rate, change in channel layout, for example from stereo to mono, and sample format conversion operations.

Android doesn’t have efficient and robust APIs for multimedia which could provide functionalities like FFmpeg. The only API which android has is  MediaCodec API, but it is faster than FFmpeg because it uses the device hardware for video processing.

Create a Small Video Editor App in Android Studio using FFmpeg

Prerequisites: 

Before we start, we need to set up an environment to run our FFmpeg commands. There are two options to do so:

  • By building our own library
  • By using any compiled source provided by the community. There are many libraries that can be used to perform FFmpeg operations in Android. For example,
    • WritingMinds
    • Bravobit
    • tanersener/mobile-ffmpeg
    • yangjie10930/EpMedia: There are many inbuilt functions present in this library from which you can clip, crop, rotate, add a logo, add a custom filter, merge different videos.

Although it is highly recommended to build your library, because that will reduce your apk size, you can add a third-party library and can update the library with time as you want. But, this process is very time consuming and requires extra skills. So, as a beginner, you can use some above-mentioned libraries and if you face some issue you can raise that issue on their respective GitHub repository. In the below example I will be using tanersener/mobile-ffmpeg, as it has support for Android 10 scoped storage, and also it is the best library available on the internet for FFmpeg mobile. A sample GIF is given below to get an idea about what we are going to do in this article. Note that we are going to implement this project using the Java language. 

Step by Step Implementation

Step 1: Create a New Project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Java as the programming language.

Step 2: Adding a dependency to the build.gradle file

We will be using tanersener/mobile-ffmpeg library to implement FFmpeg functionalities in our app. And we will also require a rangeseekbar to select the particular length of the video. So add these dependencies in build.gradle file.

implementation 'com.arthenica:mobile-ffmpeg-full:4.4'
implementation 'org.florescu.android.rangeseekbar:rangeseekbar-library:0.3.0'

Step 3: Working with the colors.xml file

Below is the code for the colors.xml file.

XML
<?xml version="1.0" encoding="utf-8"?> <resources>     <color name="colorPrimary">#3F51B5</color>     <color name="colorPost">#0091EA</color>     <color name="colorPrimaryDark">#303F9F</color>     <color name="colorAccent">#E25E14</color>     <color name="colorAccentTrans">#BEE25E14</color>      <color name="maincolor">#E25E14</color>      <color name="yellow">#feeb3c</color>     <color name="white">#fff</color>     <color name="semitranswhitecolor">#00FFFFFF</color>     <color name="colorwhite_50">#CCffffff</color>     <color name="colorwhite_10">#1Affffff</color>     <color name="colorwhite_30">#4Dffffff</color>     <color name="black">#2F2F2F</color>     <color name="graycolor">#D3D3D3</color>     <color name="graycolor2">#C5C4C4</color>     <color name="gainsboro">#DCDCDC</color>     <color name="lightgraycolor">#f2f2f2</color>     <color name="darkgray">#93959A</color>     <color name="darkgraytrans">#9493959A</color>     <color name="darkgraytransPost">#9BC5C6C9</color>     <color name="dimgray">#696969</color>     <color name="lightblack">#5d5d5d</color>      <color name="delete_message_bg">#f2f2f2</color>     <color name="delete_message_text">#b8b8b8</color>      <color name="transparent">#00ffffff</color>     <color name="fifty_transparent_black">#802F2F2F</color>      <color name="redcolor">#ff0008</color>     <color name="semitransredcolor">#93FF0008</color>     <color name="semitransredcolornew">#918E8E</color>      <color name="color_gray_alpha">#65b7b7b7</color>     <color name="app_blue">#0e1f2f</color>     <color name="text_color">#000000</color>     <color name="seekbar_color">#3be3e3</color>     <color name="line_color">#FF15FF00</color>     <color name="shadow_color">#00000000</color>      <color name="app_color">#c52127</color>  </resources> 

 
 

Step 4: Working with the activity_main.xml file

Go to the activity_main.xml file and refer to the following code. Below is the code for the activity_main.xml file. 

XML
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout     xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:background="#43AF47"     tools:context=".MainActivity">      <RelativeLayout         android:id="@+id/relative1"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_alignParentTop="true"         android:layout_margin="10dp">          <Button             android:id="@+id/cancel_button"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:layout_centerInParent="true"             android:background="@color/transparent"             android:text="Select Video"             android:textColor="@color/white" />      </RelativeLayout>       <VideoView         android:id="@+id/layout_movie_wrapper"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:layout_above="@+id/relative"         android:layout_below="@+id/relative1" />      <TextView         android:id="@+id/progressbar"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_centerInParent="true"         android:paddingBottom="5dp" />      <RelativeLayout         android:id="@+id/imagelinear"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:layout_above="@+id/relative"         android:layout_below="@+id/relative1"         android:layout_centerInParent="true">          <TextView             android:id="@+id/overlaytextview"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:layout_centerInParent="true"             android:text="Raghav"             android:textAppearance="@style/TextAppearance.AppCompat.Medium"             android:textColor="@color/white"             android:textStyle="bold"             android:visibility="gone" />          <ImageView             android:id="@+id/overlayimage"             android:layout_width="match_parent"             android:layout_height="match_parent"             android:layout_centerInParent="true"             android:scaleType="fitXY" />     </RelativeLayout>      <LinearLayout          android:id="@+id/relative"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_alignParentBottom="true"         android:orientation="vertical">          <RelativeLayout             android:layout_width="match_parent"             android:layout_height="wrap_content">              <TextView                 android:id="@+id/textleft"                 android:layout_width="wrap_content"                 android:layout_height="wrap_content"                 android:layout_alignParentStart="true"                 android:layout_marginBottom="10dp"                 android:text="00:00"                 android:textColor="@color/white" />              <TextView                 android:id="@+id/textright"                 android:layout_width="wrap_content"                 android:layout_height="wrap_content"                 android:layout_alignParentEnd="true"                 android:layout_marginBottom="10dp"                 android:layout_weight="1"                 android:text="00:00"                 android:textAlignment="textEnd"                 android:textColor="@color/white" />         </RelativeLayout>          <RelativeLayout             android:layout_width="match_parent"             android:layout_height="wrap_content"             android:background="@color/white">              <org.florescu.android.rangeseekbar.RangeSeekBar                  android:id="@+id/rangeSeekBar"                 android:layout_width="match_parent"                 android:layout_height="wrap_content"                 app:activeColor="@color/white"                 app:alwaysActive="true"                 app:barHeight="2dp"                 app:showLabels="false"                 app:textAboveThumbsColor="#000000" />          </RelativeLayout>          <LinearLayout             android:layout_width="match_parent"             android:layout_height="10dp" />          <RelativeLayout             android:layout_width="match_parent"             android:layout_height="wrap_content">              <LinearLayout                 android:id="@+id/lineartime"                 android:layout_width="match_parent"                 android:layout_height="wrap_content"                 android:orientation="vertical">                  <TextView                     android:id="@+id/text"                     android:layout_width="wrap_content"                     android:layout_height="wrap_content"                     android:layout_marginBottom="10dp"                     android:text=""                     android:textColor="@color/semitransredcolornew" />                  <LinearLayout                     android:layout_width="match_parent"                     android:layout_height="wrap_content"                     android:orientation="horizontal">                      <LinearLayout                         android:layout_width="wrap_content"                         android:layout_height="wrap_content"                         android:layout_margin="10dp"                         android:layout_weight="1"                         android:orientation="vertical">                          <ImageButton                              android:id="@+id/slow"                             android:layout_width="50dp"                             android:layout_height="50dp"                             android:layout_gravity="center"                             android:background="@color/transparent"                             android:scaleType="fitXY"                             android:src="@drawable/icon_effect_slow" />                          <TextView                             android:layout_width="match_parent"                             android:layout_height="wrap_content"                             android:text="Slow Motion"                             android:textAlignment="center"                             android:textColor="@color/white" />                     </LinearLayout>                      <LinearLayout                         android:layout_width="wrap_content"                         android:layout_height="wrap_content"                         android:layout_margin="10dp"                         android:layout_weight="1"                         android:orientation="vertical">                          <ImageButton                             android:id="@+id/reverse"                             android:layout_width="50dp"                             android:layout_height="50dp"                             android:layout_gravity="center"                             android:background="@color/transparent"                             android:scaleType="fitXY"                             android:src="@drawable/icon_effect_time" />                          <TextView                             android:layout_width="match_parent"                             android:layout_height="wrap_content"                             android:text="Reverse"                             android:textAlignment="center"                             android:textColor="@color/white" />                     </LinearLayout>                      <LinearLayout                         android:layout_width="wrap_content"                         android:layout_height="wrap_content"                         android:layout_margin="10dp"                         android:layout_weight="1"                         android:orientation="vertical">                          <ImageButton                             android:id="@+id/fast"                             android:layout_width="50dp"                             android:layout_height="50dp"                             android:layout_gravity="center"                             android:background="@color/transparent"                             android:scaleType="fitXY"                             android:src="@drawable/icon_effect_repeatedly" />                          <TextView                             android:layout_width="match_parent"                             android:layout_height="wrap_content"                             android:text="Flash"                             android:textAlignment="center"                             android:textColor="@color/white" />                     </LinearLayout>                  </LinearLayout>             </LinearLayout>              <LinearLayout                 android:id="@+id/lineareffects"                 android:layout_width="match_parent"                 android:layout_height="wrap_content"                 android:orientation="vertical">                  <TextView                     android:id="@+id/text2"                     android:layout_width="wrap_content"                     android:layout_height="wrap_content"                     android:layout_marginBottom="10dp"                     android:text="Tap to add effects"                     android:textColor="@color/white" />             </LinearLayout>          </RelativeLayout>     </LinearLayout>  </RelativeLayout> 

 
 

Step 5: Working with the MainActivity.java file

Go to the MainActivity.java file and refer to the following code. Below is the code for the MainActivity.java file. Comments are added inside the code to understand the code in more detail.

Java
import android.app.ProgressDialog; import android.content.ContentValues; import android.content.Intent; import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; import android.widget.VideoView; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.arthenica.mobileffmpeg.Config; import com.arthenica.mobileffmpeg.ExecuteCallback; import com.arthenica.mobileffmpeg.FFmpeg; import org.florescu.android.rangeseekbar.RangeSeekBar; import java.io.File; import static com.arthenica.mobileffmpeg.Config.RETURN_CODE_CANCEL; import static com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS;  public class MainActivity extends AppCompatActivity {      private ImageButton reverse, slow, fast;     private Button cancel;     private TextView tvLeft, tvRight;     private ProgressDialog progressDialog;     private String video_url;     private VideoView videoView;     private Runnable r;     private RangeSeekBar rangeSeekBar;     private static final String root = Environment.getExternalStorageDirectory().toString();     private static final String app_folder = root + "/GFG/";      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);          rangeSeekBar = (RangeSeekBar) findViewById(R.id.rangeSeekBar);         tvLeft = (TextView) findViewById(R.id.textleft);         tvRight = (TextView) findViewById(R.id.textright);         slow = (ImageButton) findViewById(R.id.slow);         reverse = (ImageButton) findViewById(R.id.reverse);         fast = (ImageButton) findViewById(R.id.fast);         cancel = (Button) findViewById(R.id.cancel_button);         fast = (ImageButton) findViewById(R.id.fast);         videoView = (VideoView) findViewById(R.id.layout_movie_wrapper);          // creating the progress dialog         progressDialog = new ProgressDialog(MainActivity.this);         progressDialog.setMessage("Please wait..");         progressDialog.setCancelable(false);         progressDialog.setCanceledOnTouchOutside(false);          // set up the onClickListeners         cancel.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 // create an intent to retrieve the video                 // file from the device storage                 Intent intent = new Intent(                         Intent.ACTION_PICK,                         android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI);                         intent.setType("video/*");                 startActivityForResult(intent, 123);             }         });          slow.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 // check if the user has selected any video or not                 // In case a user hasn't selected any video and press the button,                 // we will show an warning, stating "Please upload the video"                 if (video_url != null) {                     // a try-catch block to handle all necessary exceptions                     // like File not found, IOException                     try {                         slowmotion(rangeSeekBar.getSelectedMinValue().intValue() * 1000, rangeSeekBar.getSelectedMaxValue().intValue() * 1000);                     } catch (Exception e) {                         Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_SHORT).show();                         e.printStackTrace();                     }                 } else                     Toast.makeText(MainActivity.this, "Please upload video", Toast.LENGTH_SHORT).show();             }         });         fast.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 if (video_url != null) {                      try {                         fastforward(rangeSeekBar.getSelectedMinValue().intValue() * 1000, rangeSeekBar.getSelectedMaxValue().intValue() * 1000);                     } catch (Exception e) {                         e.printStackTrace();                         Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_SHORT).show();                     }                 } else                     Toast.makeText(MainActivity.this, "Please upload video", Toast.LENGTH_SHORT).show();             }         });         reverse.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 if (video_url != null) {                     try {                         reverse(rangeSeekBar.getSelectedMinValue().intValue() * 1000, rangeSeekBar.getSelectedMaxValue().intValue() * 1000);                     } catch (Exception e) {                         e.printStackTrace();                         Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_SHORT).show();                     }                 } else                     Toast.makeText(MainActivity.this, "Please upload video", Toast.LENGTH_SHORT).show();             }         });          // set up the VideoView.         // We will be using VideoView to view our video         videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {             @Override             public void onPrepared(MediaPlayer mp) {                 // get the duration of the video                 int duration = mp.getDuration() / 1000;                  // initially set the left TextView to "00:00:00"                 tvLeft.setText("00:00:00");                  // initially set the right Text-View to the video length                 // the getTime() method returns a formatted string in hh:mm:ss                 tvRight.setText(getTime(mp.getDuration() / 1000));                  // this will run he video in loop                 // i.e. the video won't stop                 // when it reaches its duration                 mp.setLooping(true);                  // set up the initial values of rangeSeekbar                 rangeSeekBar.setRangeValues(0, duration);                 rangeSeekBar.setSelectedMinValue(0);                 rangeSeekBar.setSelectedMaxValue(duration);                 rangeSeekBar.setEnabled(true);                  rangeSeekBar.setOnRangeSeekBarChangeListener(new RangeSeekBar.OnRangeSeekBarChangeListener() {                     @Override                     public void onRangeSeekBarValuesChanged(RangeSeekBar bar, Object minValue, Object maxValue) {                         // we seek through the video when the user                         // drags and adjusts the seekbar                         videoView.seekTo((int) minValue * 1000);                          // changing the left and right TextView according to                         // the minValue and maxValue                         tvLeft.setText(getTime((int) bar.getSelectedMinValue()));                         tvRight.setText(getTime((int) bar.getSelectedMaxValue()));                     }                 });                  // this method changes the right TextView every 1 second                 // as the video is being played                 // It works same as a time counter we see in any Video Player                 final Handler handler = new Handler();                 handler.postDelayed(r = new Runnable() {                     @Override                     public void run() {                         if (videoView.getCurrentPosition() >= rangeSeekBar.getSelectedMaxValue().intValue() * 1000)                             videoView.seekTo(rangeSeekBar.getSelectedMinValue().intValue() * 1000);                         handler.postDelayed(r, 1000);                     }                 }, 1000);             }         });     }      // Method for creating fast motion video     private void fastforward(int startMs, int endMs) throws Exception {         // startMs is the starting time, from where we have to apply the effect.         // endMs is the ending time, till where we have to apply effect.         // For example, we have a video of 5min and we only want to fast forward a part of video         // say, from 1:00 min to 2:00min, then our startMs will be 1000ms and endMs will be 2000ms.         // create a progress dialog and show it until this method executes.         progressDialog.show();          // creating a new file in storage         final String filePath;         String filePrefix = "fastforward";         String fileExtn = ".mp4";         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {             // With introduction of scoped storage in Android Q the primitive method gives error             // So, it is recommended to use the below method to create a video file in storage.             ContentValues valuesvideos = new ContentValues();             valuesvideos.put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/" + "Folder");             valuesvideos.put(MediaStore.Video.Media.TITLE, filePrefix + System.currentTimeMillis());             valuesvideos.put(MediaStore.Video.Media.DISPLAY_NAME, filePrefix + System.currentTimeMillis() + fileExtn);             valuesvideos.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");             valuesvideos.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);             valuesvideos.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());             Uri uri = getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, valuesvideos);              // get the path of the video file created in the storage.             File file = FileUtils.getFileFromUri(this, uri);             filePath = file.getAbsolutePath();          } else {             // This else statement will work for devices with Android version lower than 10             // Here, "app_folder" is the path to your app's root directory in device storage             File dest = new File(new File(app_folder), filePrefix + fileExtn);             int fileNo = 0;             // check if the file name previously exist. Since we don't want             // to overwrite the video files             while (dest.exists()) {                 fileNo++;                 dest = new File(new File(app_folder), filePrefix + fileNo + fileExtn);             }             // Get the filePath once the file is successfully created.             filePath = dest.getAbsolutePath();         }         String exe;         // the "exe" string contains the command to process video.The details of command are discussed later in this post.         // "video_url" is the url of video which you want to edit. You can get this url from intent by selecting any video from gallery.         exe = "-y -i " + video_url + " -filter_complex [0:v]trim=0:" + startMs / 1000 + ",setpts=PTS-STARTPTS[v1];[0:v]trim=" + startMs / 1000 + ":" + endMs / 1000 + ",setpts=0.5*(PTS-STARTPTS)[v2];[0:v]trim=" + (endMs / 1000) + ",setpts=PTS-STARTPTS[v3];[0:a]atrim=0:" + (startMs / 1000) + ",asetpts=PTS-STARTPTS[a1];[0:a]atrim=" + (startMs / 1000) + ":" + (endMs / 1000) + ",asetpts=PTS-STARTPTS,atempo=2[a2];[0:a]atrim=" + (endMs / 1000) + ",asetpts=PTS-STARTPTS[a3];[v1][a1][v2][a2][v3][a3]concat=n=3:v=1:a=1 " + "-b:v 2097k -vcodec mpeg4 -crf 0 -preset superfast " + filePath;          // Here, we have used he Async task to execute our query because         // if we use the regular method the progress dialog         // won't be visible. This happens because the regular method and         // progress dialog uses the same thread to execute         // and as a result only one is a allowed to work at a time.         // By using we Async task we create a different thread which resolves the issue.         long executionId = FFmpeg.executeAsync(exe, new ExecuteCallback() {             @Override             public void apply(final long executionId, final int returnCode) {                 if (returnCode == RETURN_CODE_SUCCESS) {                     // after successful execution of ffmpeg command,                     // again set up the video Uri in VideoView                     videoView.setVideoURI(Uri.parse(filePath));                      // change the video_url to filePath, so that we could                     // do more manipulations in the                     // resultant video. By this we can apply as many effects                     // as we want in a single video.                     // Actually there are multiple videos being formed in                     // storage but while using app it                     // feels like we are doing manipulations in only one video                     video_url = filePath;                     // play the result video in VideoView                     videoView.start();                     // remove the progress dialog                     progressDialog.dismiss();                 } else if (returnCode == RETURN_CODE_CANCEL) {                     Log.i(Config.TAG, "Async command execution cancelled by user.");                 } else {                     Log.i(Config.TAG, String.format("Async command execution failed with returnCode=%d.", returnCode));                 }             }         });     }      // Method for creating slow motion video for specific part of the video     // The below code is same as above only the command in string "exe" is changed     private void slowmotion(int startMs, int endMs) throws Exception {          progressDialog.show();         final String filePath;         String filePrefix = "slowmotion";         String fileExtn = ".mp4";         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {              ContentValues valuesvideos = new ContentValues();             valuesvideos.put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/" + "Folder");             valuesvideos.put(MediaStore.Video.Media.TITLE, filePrefix + System.currentTimeMillis());             valuesvideos.put(MediaStore.Video.Media.DISPLAY_NAME, filePrefix + System.currentTimeMillis() + fileExtn);             valuesvideos.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");             valuesvideos.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);             valuesvideos.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());             Uri uri = getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, valuesvideos);             File file = FileUtils.getFileFromUri(this, uri);             filePath = file.getAbsolutePath();          } else {             File dest = new File(new File(app_folder), filePrefix + fileExtn);             int fileNo = 0;             while (dest.exists()) {                 fileNo++;                 dest = new File(new File(app_folder), filePrefix + fileNo + fileExtn);             }             filePath = dest.getAbsolutePath();         }         String exe;         exe = "-y -i " + video_url + " -filter_complex [0:v]trim=0:" + startMs / 1000 + ",setpts=PTS-STARTPTS[v1];[0:v]trim=" + startMs / 1000 + ":" + endMs / 1000 + ",setpts=2*(PTS-STARTPTS)[v2];[0:v]trim=" + (endMs / 1000) + ",setpts=PTS-STARTPTS[v3];[0:a]atrim=0:" + (startMs / 1000) + ",asetpts=PTS-STARTPTS[a1];[0:a]atrim=" + (startMs / 1000) + ":" + (endMs / 1000) + ",asetpts=PTS-STARTPTS,atempo=0.5[a2];[0:a]atrim=" + (endMs / 1000) + ",asetpts=PTS-STARTPTS[a3];[v1][a1][v2][a2][v3][a3]concat=n=3:v=1:a=1 " + "-b:v 2097k -vcodec mpeg4 -crf 0 -preset superfast " + filePath;          long executionId = FFmpeg.executeAsync(exe, new ExecuteCallback() {              @Override             public void apply(final long executionId, final int returnCode) {                 if (returnCode == RETURN_CODE_SUCCESS) {                      videoView.setVideoURI(Uri.parse(filePath));                     video_url = filePath;                     videoView.start();                     progressDialog.dismiss();                  } else if (returnCode == RETURN_CODE_CANCEL) {                     Log.i(Config.TAG, "Async command execution cancelled by user.");                 } else {                     Log.i(Config.TAG, String.format("Async command execution failed with returnCode=%d.", returnCode));                 }             }         });     }      // Method for reversing the video     // The below code is same as above only the command is changed.     private void reverse(int startMs, int endMs) throws Exception {         progressDialog.show();         String filePrefix = "reverse";         String fileExtn = ".mp4";          final String filePath;         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {              ContentValues valuesvideos = new ContentValues();             valuesvideos.put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/" + "Folder");             valuesvideos.put(MediaStore.Video.Media.TITLE, filePrefix + System.currentTimeMillis());             valuesvideos.put(MediaStore.Video.Media.DISPLAY_NAME, filePrefix + System.currentTimeMillis() + fileExtn);             valuesvideos.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");             valuesvideos.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);             valuesvideos.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());             Uri uri = getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, valuesvideos);             File file = FileUtils.getFileFromUri(this, uri);             filePath = file.getAbsolutePath();          } else {             filePrefix = "reverse";             fileExtn = ".mp4";             File dest = new File(new File(app_folder), filePrefix + fileExtn);             int fileNo = 0;             while (dest.exists()) {                 fileNo++;                 dest = new File(new File(app_folder), filePrefix + fileNo + fileExtn);             }             filePath = dest.getAbsolutePath();         }          long executionId = FFmpeg.executeAsync("-y -i " + video_url + " -filter_complex [0:v]trim=0:" + endMs / 1000 + ",setpts=PTS-STARTPTS[v1];[0:v]trim=" + startMs / 1000 + ":" + endMs / 1000 + ",reverse,setpts=PTS-STARTPTS[v2];[0:v]trim=" + (startMs / 1000) + ",setpts=PTS-STARTPTS[v3];[v1][v2][v3]concat=n=3:v=1 " + "-b:v 2097k -vcodec mpeg4 -crf 0 -preset superfast " + filePath, new ExecuteCallback() {             @Override             public void apply(final long executionId, final int returnCode) {                 if (returnCode == RETURN_CODE_SUCCESS) {                     videoView.setVideoURI(Uri.parse(filePath));                     video_url = filePath;                     videoView.start();                     progressDialog.dismiss();                 } else if (returnCode == RETURN_CODE_CANCEL) {                     Log.i(Config.TAG, "Async command execution cancelled by user.");                 } else {                     Log.i(Config.TAG, String.format("Async command execution failed with returnCode=%d.", returnCode));                 }             }         });     }      // Overriding the method onActivityResult()      // to get the video Uri form intent.     @Override     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {         super.onActivityResult(requestCode, resultCode, data);          if (resultCode == RESULT_OK) {             if (requestCode == 123) {                 if (data != null) {                     // get the video Uri                     Uri uri = data.getData();                     try {                         // get the file from the Uri using getFileFromUri() method present                         // in FileUils.java                         File video_file = FileUtils.getFileFromUri(this, uri);                                                  // now set the video uri in the VideoView                         videoView.setVideoURI(uri);                                                  // after successful retrieval of the video and properly                         // setting up the retried video uri in                         // VideoView, Start the VideoView to play that video                         videoView.start();                                                  // get the absolute path of the video file. We will require                         // this as an input argument in                         // the ffmpeg command.                         video_url = video_file.getAbsolutePath();                     } catch (Exception e) {                         Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();                         e.printStackTrace();                     }                 }             }         }     }      // This method returns the seconds in hh:mm:ss time format     private String getTime(int seconds) {         int hr = seconds / 3600;         int rem = seconds % 3600;         int mn = rem / 60;         int sec = rem % 60;         return String.format("%02d", hr) + ":" + String.format("%02d", mn) + ":" + String.format("%02d", sec);     } } 


Step 6: Creating a new Java Class FileUtils.java

Refer to How to Create Classes in Android Studio to create a new java class in Android Studio. This is a Utility file that will help in retrieving the File from a Uri. Below is the code for the FileUtils.java file. Comments are added inside the code to understand the code in more detail.

Java
import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; import java.io.File;  public class FileUtils {      // Get a file from a Uri.     // Framework Documents, as well as the _data field for the MediaStore and     // other file-based ContentProviders.     // @param context The context.     // @param uri     The Uri to query     public static File getFileFromUri(final Context context, final Uri uri) throws Exception {          String path = null;          // DocumentProvider         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {             if (DocumentsContract.isDocumentUri(context, uri)) { // TODO: 2015. 11. 17. KITKAT                  // ExternalStorageProvider                 if (isExternalStorageDocument(uri)) {                     final String docId = DocumentsContract.getDocumentId(uri);                     final String[] split = docId.split(":");                     final String type = split[0];                      if ("primary".equalsIgnoreCase(type)) {                         path = Environment.getExternalStorageDirectory() + "/" + split[1];                     }                      // TODO handle non-primary volumes                  } else if (isDownloadsDocument(uri)) { // DownloadsProvider                     final String id = DocumentsContract.getDocumentId(uri);                     final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));                     path = getDataColumn(context, contentUri, null, null);                 } else if (isMediaDocument(uri)) { // MediaProvider                     final String docId = DocumentsContract.getDocumentId(uri);                     final String[] split = docId.split(":");                     final String type = split[0];                      Uri contentUri = null;                     if ("image".equals(type)) {                         contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;                     } else if ("video".equals(type)) {                         contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;                     } else if ("audio".equals(type)) {                         contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;                     }                      final String selection = "_id=?";                     final String[] selectionArgs = new String[]{                             split[1]                     };                     path = getDataColumn(context, contentUri, selection, selectionArgs);                 }  // MediaStore (and general)             } else if ("content".equalsIgnoreCase(uri.getScheme())) {                 path = getDataColumn(context, uri, null, null);             }             // File             else if ("file".equalsIgnoreCase(uri.getScheme())) {                 path = uri.getPath();             }             return new File(path);         } else {             Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);             return new File(cursor.getString(cursor.getColumnIndex("_data")));         }     }      // Get the value of the data column for this Uri. This is useful for     // MediaStore Uris, and other file-based ContentProviders.     // @param context       The context.     // @param uri           The Uri to query.     // @param selection     (Optional) Filter used in the query.     // @param selectionArgs (Optional) Selection arguments used in the query.     // @return The value of the _data column, which is typically a file path.     public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {         Cursor cursor = null;         final String column = MediaStore.Images.Media.DATA;         final String[] projection = {                 column         };          try {             cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);             if (cursor != null && cursor.moveToFirst()) {                 final int column_index = cursor.getColumnIndexOrThrow(column);                 return cursor.getString(column_index);             }         } finally {             if (cursor != null)                 cursor.close();         }         return null;     }       // @param uri The Uri to check.     // @return Whether the Uri authority is ExternalStorageProvide     public static boolean isExternalStorageDocument(Uri uri) {         return "com.android.externalstorage.documents".equals(uri.getAuthority());     }      // @param uri The Uri to check.     // @return Whether the Uri authority is DownloadsProvider.     public static boolean isDownloadsDocument(Uri uri) {         return "com.android.providers.downloads.documents".equals(uri.getAuthority());     }      // @param uri The Uri to check.     // @return Whether the Uri authority is MediaProvider.     public static boolean isMediaDocument(Uri uri) {         return "com.android.providers.media.documents".equals(uri.getAuthority());     } } 

Output:


 

Github Project Link: https://github.com/raghavtilak/VideoEditor

Few FFmpeg commands that you can play with

  • concat video of different framerates in .mkv format:
    • -i input1.mp4 -i input2.mp4 -filter_complex [0:v:0][0:a:0][1:v:0][1:a:0]concat=n=2:v=1:a=1[outv][outa] -map [outv] -map [outa] output.mkv
  • concat video with no sound/audio:
    • -y -i input.mp4 -filter_complex [0:v]trim=0:0,setpts=PTS-STARTPTS[v1];[0:v]trim=0:5,setpts=0.5*(PTS-STARTPTS)[v2];[0:v]trim=5,setpts=PTS-STARTPTS[v3];[v1][v2][v3]concat=n=3:v=1:a=0 -b:v 2097k -vcodec mpeg4 -crf 0 -preset superfast output.mp4
  • Textoverlay:
    • -y -i input.mp4 -vf drawtext="fontsize=30:fontfile=cute.ttf:text='GFG'":x=w-tw-10:y=h-th-10 -c:v libx264 -preset ultrafast outputmp4
  • gif/png/jpeg overlay
    • -i input.mp4 -i inputimage.png -filter_complex [1:v]scale=320:394[ovr1],[0:v][ovr1]overlay=0:0:enable='between(t,0,16)' -c:a copy output.mp4, (or)
    • -i input.mp4 -i inputimage.png -filter_complex overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2:enable='between(t,0,7)' -c:a copy output.mp4
  • Add subtitles to a video file
    • -i input.mp4 -i subtitle.srt -map 0 -map 1 -c copy -c:v libx264 -crf 23 -preset superfast output.mp4
  • Converting video files to audio files
    • -i input.mp4 -vn output.mp3
  • Cropping videos
    • -i input.mp4 -filter:v "crop=w:h:x:y" output.mp4
      • w - width of the rectangle which we are intended to crop from the source video.
      • h - the height of that rectangle.
      • x - the x coordinate of that rectangle.
      • y - the y coordinate of the rectangle.
  • Adding a poster image to audio files
    • -loop 1 -i inputimage.jpg -i inputaudio.mp3 -c:v libx264 -c:a aac -strict experimental -b:a 192k -shortest output.mp4

Pros of using FFmpeg

  1. It is also highly portable.
  2. It is profoundly valuable for the transcoding of all kinds of multimedia files into a single common format.
  3. You don’t need heavy Third-party VideoEditors like Adobe Premiere Pro, Filmora for small editing tasks.

Cons of using FFmpeg

  1. It’s difficult for beginners to use and implement.
  2. It takes some time to process. We don’t get results in a second or two.
  3. The official documentation is quite confusing and it's not beginner-friendly.
  4. APK size becomes very large. The FFmpeg libraries alone will use 30-70MB depending upon the libraries you are including.

Alternatives of FFmpeg

  • MediaCodec Android
  • LiTr
  • Gstreamer
  • MP4Parser
  • Intel INDE Media for Mobile

Notes:

1. If you set -preset to a higher value say, ultrafast then the video processing will be fast but the quality of the video will be compromised. The lower the -preset higher the quality of the video.

2. You can change the -crf value to change the quality of the output video. Lower the crf value higher the quality of the video.

3. If you use -y in starting of command then this means that if a file is present with the same name as that of the output file name that FFmpeg will overwrite the existing file.

4. In the case of video, to slow down the video set -PTS value larger than 1. The larger the value slower the video, Lower the value Faster the video. But in the case of Audio this is just the opposite, i.e. Larger the value faster the Audio, the Lower the value slower the audio.

5. The atempo(audio) filter is limited to using values between 0.5 and 2.0 (so it can slow it down to no less than half the original speed, and speed up to no more than double the input)

6. FFmpeg takes too much time working with audio. If the video file doesn't contain the audio we need not to command FFmpeg to work with audio, and hence this will reduce the workload and we will get the processed video fast/in less time.


Next Article
How to Use FFmpeg in Android with Example?

R

raghav14
Improve
Article Tags :
  • Java
  • Technical Scripter
  • Android
  • Technical Scripter 2020
  • Android Projects
Practice Tags :
  • Java

Similar Reads

    How to Use Animated GIF in Android App?
    In this article, we are going to show an animated gif in our project using a library. There are many methods to show a gif. We can also show a gif using WebView. Here we are going to use this library to show the gif. So here we are going to learn how to implement that feature. A sample GIF is given
    3 min read
    ExoPlayer in Android with Example
    ExoPlayerView is one of the most used UI components in many apps such as YouTube, Netflix, and many video streaming platforms. ExoPlayerView is used for audio as well as video streaming in Android apps. Many Google apps use ExoPlayerView for streaming audios and videos. ExoPlayer is a media player l
    4 min read
    Audio Recorder in Android with Example
    In Android for recording audio or video, there is a built-in class called MediaRecorder. This class in Android helps to easily record video and audio files. The Android multimedia framework provides built-in support for capturing and encoding common audio and video formats. In android for recording
    15+ min read
    How to Apply View Animations Effects in Android?
    Android View Animations are used to apply amazing animations on TextView and EditText in the android application. Such animations provide the app with a smooth look in a new way. In this article, we are going to develop the Android View Animation effect in Android Studio. What we are going to build
    3 min read
    How to Play Video from URL in Android?
    In this article, you will see how to play a video from a URL on Android. For showing the video in our Android application we will use the VideoView widget. The VideoView widget is capable of playing media files, and the formats supported by the VideoView are 3gp and MP4. By using VideoView you can p
    3 min read
geeksforgeeks-footer-logo
Corporate & Communications Address:
A-143, 7th Floor, Sovereign Corporate Tower, Sector- 136, Noida, Uttar Pradesh (201305)
Registered Address:
K 061, Tower K, Gulshan Vivante Apartment, Sector 137, Noida, Gautam Buddh Nagar, Uttar Pradesh, 201305
GFG App on Play Store GFG App on App Store
Advertise with us
  • Company
  • About Us
  • Legal
  • Privacy Policy
  • In Media
  • Contact Us
  • Advertise with us
  • GFG Corporate Solution
  • Placement Training Program
  • Languages
  • Python
  • Java
  • C++
  • PHP
  • GoLang
  • SQL
  • R Language
  • Android Tutorial
  • Tutorials Archive
  • DSA
  • Data Structures
  • Algorithms
  • DSA for Beginners
  • Basic DSA Problems
  • DSA Roadmap
  • Top 100 DSA Interview Problems
  • DSA Roadmap by Sandeep Jain
  • All Cheat Sheets
  • Data Science & ML
  • Data Science With Python
  • Data Science For Beginner
  • Machine Learning
  • ML Maths
  • Data Visualisation
  • Pandas
  • NumPy
  • NLP
  • Deep Learning
  • Web Technologies
  • HTML
  • CSS
  • JavaScript
  • TypeScript
  • ReactJS
  • NextJS
  • Bootstrap
  • Web Design
  • Python Tutorial
  • Python Programming Examples
  • Python Projects
  • Python Tkinter
  • Python Web Scraping
  • OpenCV Tutorial
  • Python Interview Question
  • Django
  • Computer Science
  • Operating Systems
  • Computer Network
  • Database Management System
  • Software Engineering
  • Digital Logic Design
  • Engineering Maths
  • Software Development
  • Software Testing
  • DevOps
  • Git
  • Linux
  • AWS
  • Docker
  • Kubernetes
  • Azure
  • GCP
  • DevOps Roadmap
  • System Design
  • High Level Design
  • Low Level Design
  • UML Diagrams
  • Interview Guide
  • Design Patterns
  • OOAD
  • System Design Bootcamp
  • Interview Questions
  • Inteview Preparation
  • Competitive Programming
  • Top DS or Algo for CP
  • Company-Wise Recruitment Process
  • Company-Wise Preparation
  • Aptitude Preparation
  • Puzzles
  • School Subjects
  • Mathematics
  • Physics
  • Chemistry
  • Biology
  • Social Science
  • English Grammar
  • Commerce
  • World GK
  • GeeksforGeeks Videos
  • DSA
  • Python
  • Java
  • C++
  • Web Development
  • Data Science
  • CS Subjects
@GeeksforGeeks, Sanchhaya Education Private Limited, All rights reserved
We use cookies to ensure you have the best browsing experience on our website. By using our site, you acknowledge that you have read and understood our Cookie Policy & Privacy Policy
Lightbox
Improvement
Suggest Changes
Help us improve. Share your suggestions to enhance the article. Contribute your expertise and make a difference in the GeeksforGeeks portal.
geeksforgeeks-suggest-icon
Create Improvement
Enhance the article with your expertise. Contribute to the GeeksforGeeks community and help create better learning resources for all.
geeksforgeeks-improvement-icon
Suggest Changes
min 4 words, max Words Limit:1000

Thank You!

Your suggestions are valuable to us.

What kind of Experience do you want to share?

Interview Experiences
Admission Experiences
Career Journeys
Work Experiences
Campus Experiences
Competitive Exam Experiences