Добавление видео визуального фильтра в mp4 - PullRequest
0 голосов
/ 03 сентября 2018

Я пытаюсь добавить visual filters к video в Android. Это должно выглядеть как то, что есть в Instagram: после записи video вы можете выбрать visual filter из списка и затем применить его. На данный момент лучшее, что я нашел, это GPUImage, в котором есть опция нескольких фильтров, но она может быть использована только для изображений.

После записи видео я создаю файл .mp4 в папке temp и перед его загрузкой открывается экран, аналогичный изображенному ниже. И мне нужно было бы создать аналогичную опцию фильтра и добавить фильтр.

image

Есть ли какие-нибудь API, которые могут мне помочь или у кого-то есть исходный код?

Ответы [ 4 ]

0 голосов
/ 12 сентября 2018

Мне понадобилось время, но я понял это, используя FFmpeg. Так как мой проект уже использовал bravobit FFmpeg ( Bravobit ffmpeg ), я решил придерживаться его. Я добавил весь код, который понадобился мне, чтобы создать его, на случай, если кто-то упадет в одном месте.

Сначала я должен был сделать 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>

И создайте 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>

Затем я получаю миниатюру из своего видео File и вызываю метод, используя эту миниатюру:

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;
}

А теперь, наконец, код для создания и использования фильтров:

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();
    }
}

Сначала я испортился .mp4, потому что я не добавил "-pix_fmt", "yuv420p". Подробнее об этом вы можете прочитать здесь: FFmpeg видеофильтры поврежденный файл mp4

0 голосов
/ 11 сентября 2018

Вы пробовали этот ? он использует FFMPEG для добавления фильтров / обрезки и других функций редактирования, это может помочь вам как библиотеке и дать вам идею, у него есть демонстрационное приложение, построенное с этой библиотекой, доступное в play store здесь

0 голосов
/ 12 сентября 2018

Помимо способов FFMPEG, я нашел полезным для себя использование GLSurfaceView . Идея состоит в том, чтобы визуализировать видео в GLSurfaceView и отрисовать фильтры, используя openGL. Проверьте этот проект .

0 голосов
/ 05 сентября 2018

Вы должны перекодировать файл mp4, чтобы применить фильтр к каждому кадру. Я могу придумать два способа сделать это, но они требуют продвинутых навыков программирования. Я думаю, что самый простой способ - это FFMPEG (не забудьте проверить лицензии, если хотите перекодировать). Эта ссылка может помочь вам скомпилировать ее для Android. Как только это будет сделано, проверьте документацию FFMPEG и форумы для фильтров и оверлеев. Другой (бесплатный) способ - использовать MediaCodec для перекодирования видео и использовать шейдеры GL для управления кадрами. Grafika - проект, который может предоставить вам необходимые инструменты для этого. Кроме того, в Интернете могут быть предварительно собранные библиотеки обоих способов, убедитесь, что использовали данную информацию, чтобы сначала провести исследование.

...