Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding Video Visual Filter to mp4

I am trying to add visual filters to video in Android. It should look like something Instagram has, that after recording a video, you can choose a visual filter from the list and then apply it. So far the best I have found is GPUImage that has multiple filter option but it can only be used on images.

After recording video, I create a .mp4 file into temp folder and before uploading it, similar screen to the picture below opens up. And I would need to create a similar filter option and filter addition.

Is there some API available that could help me or does someone have a source code?

like image 556
Richard Avatar asked Sep 03 '18 09:09

Richard


People also ask

How do I add a filter to an already made video?

Figuring out how to add filters to videos you have on apps like Photos on iPhone or Camera on Android is simple. Just film your vid on your phone, then pop into your photo app. Click “Edit.” Adjust brightness, contrast, or try one of your phone's existing filters, whether that's in the Photos App or Google Photos.

Can you put a face filter on a video?

Can you put a filter on your video? Yes! There are many ways to apply a video face filter to your videos with easy to use mobile apps for iPhone and Android like YouCam Video. YouCam Video has a range of video filters, face filters, hair color filters, makeup filters and much more!


4 Answers

Have you tried this one ? it uses FFMPEG to add filters / crop and more editing features, this can help you as a library and can give you an idea as well, it has a demo app built with this library available at play store here

like image 191
Ashfaque Ali Solangi Avatar answered Sep 19 '22 01:09

Ashfaque Ali Solangi


Alongside the FFMPEG ways, the one I found useful for myself is to use GLSurfaceView. The idea is to render the video on the GLSurfaceView and render the filters using openGL. Check out this project.

like image 31
Udit Mukherjee Avatar answered Sep 20 '22 01:09

Udit Mukherjee


Took me a while but I figured it out using FFmpeg. Sine my project was already using bravobit FFmpeg (Bravobit ffmpeg) I decided to stick with it. I have addded all the code it took me to create this just in case someone tumbles at the same place.

First I had to make a Horizontal scrollview:

<HorizontalScrollView
    android:layout_width="wrap_content"
    android:layout_height="150dp"
    android:layout_alignParentBottom="true"
    android:layout_alignParentStart="true"
    android:layout_gravity="center_vertical"
    android:layout_marginBottom="143dp"
    android:scrollbars="none">

    <LinearLayout
        android:id="@+id/filter_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:orientation="horizontal"></LinearLayout>
</HorizontalScrollView>

And create a filter_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="95dp"
    android:layout_height="wrap_content"

    android:layout_marginLeft="4dp"
    android:layout_marginStart="4dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/filter_item_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:textColor="#ff0000"
        android:textStyle="bold"
        android:textSize="14dp" />


    <android.support.v7.widget.AppCompatImageView
        android:id="@+id/filter_item_image"
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:layout_below="@+id/filter_item_name"
        android:scaleType="centerCrop" />


</RelativeLayout>

Next I get a thumbnail from my video File and call a method using that thumbnail:

shareToFragment.setThumbNailImage(getVideoThumbnail(cameraOutputFile.getPath()));

public static Bitmap getVideoThumbnail(String path) {
    Bitmap bitmap = null;

    FFmpegMediaMetadataRetriever fmmr = new FFmpegMediaMetadataRetriever();

    try {
        fmmr.setDataSource(path);

        final byte[] data = fmmr.getEmbeddedPicture();

        if (data != null) {
            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        }

        if (bitmap == null) {
            bitmap = fmmr.getFrameAtTime();
        }
    } catch (Exception e) {
        bitmap = null;
    } finally {
        fmmr.release();
    }
    return bitmap;
}

And now finally the code for creating and using the filters:

String[] filters = new String[] {"colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131",  "curves=vintage", "curves=negative", "hue=s=0"};
String[] filterNames = new String[] {"Sepia",  "Vintage", "Negative", "Black/White"};
int loopCounter;

public void setThumbNailImage(Bitmap image) {
    loopCounter = -1;
    mGallery.setVisibility(View.VISIBLE);
    LayoutInflater mInflater = LayoutInflater.from(getActivity());

    ffmpeg = FFmpeg.getInstance(context);

    createNewFileForImageAndVideoNoFilter();
    bitmapToFile(image);

    addFilter(mInflater);

}

private void addFilter(LayoutInflater mInflater){
    loopCounter++;
    View view = mInflater.inflate(R.layout.filter_item,
            mGallery, false);

    createNewFileForFilteredImage();



    String[] cmd = { "-i",  imageToBeFiltered.toString(), "-filter_complex", filters[loopCounter], imageWithFilter.toString()};

    ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler() {
        @Override
        public void onSuccess(String message) {
            super.onSuccess(message);

            Bitmap image = BitmapFactory.decodeFile(imageWithFilter.getAbsolutePath());
            ImageView img = (ImageView) view.findViewById(R.id.filter_item_image);
            img.setImageBitmap(image);
            mGallery.addView(view);
            TextView txt = (TextView) view.findViewById(R.id.filter_item_name);
            txt.setText(filterNames[loopCounter]);
            view.setId(loopCounter);
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    filteredVideo.delete();
                    String[] cmd = { "-i",  originalVideoFile.toString(), "-filter_complex", filters[view.getId()], "-pix_fmt", "yuv420p",  filteredVideo.toString()};
                    ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler() {
                        @Override
                        public void onSuccess(String message) {
                            super.onSuccess(message);
                            System.out.println("ffmpegVideo: succ" + message);
                            Toast.makeText(context, "Done with adding flter", Toast.LENGTH_LONG).show();
                            somethingYouWannaDoWithTheOutputFile();
                        }
                        @Override
                        public void onFailure(String message) {
                            super.onFailure(message);
                            System.out.println("ffmpegVideo: faill" + message);
                        }

                        @Override
                        public void onProgress(String message) {
                            super.onProgress(message);
                            Toast.makeText(context, "Adding filter", Toast.LENGTH_LONG).show();
                        }
                    });

                }
            });

            if (loopCounter+1 < filters.length) addFilter(mInflater);
        }
        @Override
        public void onFailure(String message) {
            super.onFailure(message);
            if (loopCounter+1 < filters.length) addFilter(mInflater);
        }
    });
}


public void createNewFileForImageAndVideoNoFilter(){
    filteredVideo = new File(context.getFilesDir()+ Common.TEMP_LOCAL_DIR + "/" + "filteredVideo.mp4");
    imageToBeFiltered = new File(context.getFilesDir()+ Common.TEMP_LOCAL_DIR + "/" + "_imageToBeFiltered.png");
    if(filteredVideo.exists()){
        filteredVideo.delete();
        imageToBeFiltered.delete();
    }
}

private void bitmapToFile(Bitmap bitmap){
    try {
        OutputStream os = new BufferedOutputStream(new FileOutputStream(imageToBeFiltered));
        bitmap.compress(Bitmap.CompressFormat.PNG, 0, os);
        os.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

private void createNewFileForFilteredImage(){
    imageWithFilter = new File(context.getFilesDir()+ Common.TEMP_LOCAL_DIR + "/" + "_filteredImage.png");
    if(imageWithFilter.exists()){
        imageWithFilter.delete();
    }
}

At first I got corrupted .mp4 because I wasn't adding "-pix_fmt", "yuv420p". You can read more about it here : FFmpeg video filters corrupt mp4 file

like image 37
Richard Avatar answered Sep 18 '22 01:09

Richard


You have to re-encode the mp4 file in order to apply the filter to every frame. I can think of two ways of doing that, but they require advanced programming skills. The easiest way I think is FFMPEG (make sure to check the licenses if you want to re-encode). This link might help you to compile it for Android. Once that's done, check out the the FFMPEG documentation and forums for filters and overlays. The other (free) way is using MediaCodec to re-encode your video, and use GL shaders to manipulate your frames. Grafika is a project that may provide you with the necessary tools for this. Also, there might be pre-built libraries of both ways on the internet, make sure to use the given information to do your research first.

like image 34
Endre Börcsök Avatar answered Sep 20 '22 01:09

Endre Börcsök