How to Use FFmpeg in Android with Example? - GeeksforGeeks

08 Oct, 2021
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.
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.
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,WritingMindsBravobittanersener/mobile-ffmpegyangjie10930/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.
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 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
concate 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
-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
concate 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
-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
-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
-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
-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
-i input.mp4 -vn output.mp3
Cropping videos-i input.mp4 -filter:v “crop=w:h:x:y” output.mp4w – 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.
-i input.mp4 -filter:v “crop=w:h:x:y” output.mp4w – 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.
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
-loop 1 -i inputimage.jpg -i inputaudio.mp3 -c:v libx264 -c:a aac -strict experimental -b:a 192k -shortest output.mp4
It is also highly portable.It is profoundly valuable for the transcoding of all kinds of multimedia files into a single common format.You don’t need heavy Third-party VideoEditors like Adobe Premiere Pro, Filmora for small editing tasks.
It is also highly portable.
It is profoundly valuable for the transcoding of all kinds of multimedia files into a single common format.
You don’t need heavy Third-party VideoEditors like Adobe Premiere Pro, Filmora for small editing tasks.
It’s difficult for beginners to use and implement.It takes some time to process. We don’t get results in a second or two.The official documentation is quite confusing and it’s not beginner-friendly.APK size becomes very large. The FFmpeg libraries alone will use 30-70MB depending upon the libraries you are including.
It’s difficult for beginners to use and implement.
It takes some time to process. We don’t get results in a second or two.
The official documentation is quite confusing and it’s not beginner-friendly.
APK size becomes very large. The FFmpeg libraries alone will use 30-70MB depending upon the libraries you are including.
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.
varshagumber28
prachisoda1234
android
Technical Scripter 2020
Android
Java
Technical Scripter
Java
Android
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Comments
Old Comments
Broadcast Receiver in Android With Example
How to Create and Add Data to SQLite Database in Android?
Content Providers in Android with Example
Android RecyclerView in Kotlin
Navigation Drawer in Android
Arrays in Java
Split() String method in Java with examples
For-each loop in Java
Reverse a string in Java
HashMap in Java with Examples