将视频视觉过滤器添加到 mp4
Adding Video Visual Filter to mp4
我正在尝试将 visual filters
添加到 Android
中的 video
。它应该看起来像 Instagram 有的东西,在记录 video
之后,您可以从列表中选择一个 visual filter
然后应用它。到目前为止,我发现的最好的是 GPUImage
,它有多个过滤器选项,但它只能用于图像。
录制视频后,我在 temp
文件夹中创建了一个 .mp4
文件,在上传之前,会打开与下图类似的屏幕。而且我需要创建一个类似的过滤器选项和过滤器添加。
是否有一些 API
可以帮助我,或者有人有源代码吗?
您必须重新编码 mp4 文件才能将滤镜应用于每一帧。我可以想到两种方法,但它们需要高级编程技能。我认为最简单的方法是 FFMPEG(如果要重新编码,请务必检查许可证)。 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 是一个可以为此提供必要工具的项目。此外,互联网上可能有两种方式的预建库,请务必先使用给定的信息进行研究。
你试过了吗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
除了 FFMPEG 方法外,我发现对自己有用的方法是使用 GLSurfaceView. The idea is to render the video on the GLSurfaceView and render the filters using openGL. Check out this project。
花了我一段时间,但我用 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"
。您可以在这里阅读更多相关信息:
我正在尝试将 visual filters
添加到 Android
中的 video
。它应该看起来像 Instagram 有的东西,在记录 video
之后,您可以从列表中选择一个 visual filter
然后应用它。到目前为止,我发现的最好的是 GPUImage
,它有多个过滤器选项,但它只能用于图像。
录制视频后,我在 temp
文件夹中创建了一个 .mp4
文件,在上传之前,会打开与下图类似的屏幕。而且我需要创建一个类似的过滤器选项和过滤器添加。
是否有一些 API
可以帮助我,或者有人有源代码吗?
您必须重新编码 mp4 文件才能将滤镜应用于每一帧。我可以想到两种方法,但它们需要高级编程技能。我认为最简单的方法是 FFMPEG(如果要重新编码,请务必检查许可证)。 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 是一个可以为此提供必要工具的项目。此外,互联网上可能有两种方式的预建库,请务必先使用给定的信息进行研究。
你试过了吗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
除了 FFMPEG 方法外,我发现对自己有用的方法是使用 GLSurfaceView. The idea is to render the video on the GLSurfaceView and render the filters using openGL. Check out this project。
花了我一段时间,但我用 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"
。您可以在这里阅读更多相关信息: