我正在尝试将visual filters
中的video
添加到Android
中。看起来Instagram具有某种功能,在录制video
之后,您可以从列表中选择visual filter
,然后应用它。到目前为止,我发现最好的是GPUImage
,它具有多个过滤器选项,但只能在图像上使用。
录制视频后,我将一个.mp4
文件创建到temp
文件夹中,然后再将其上传之前,将打开类似于下图的屏幕。而且,我需要创建一个类似的过滤器选项和添加过滤器。
是否有一些API
可以帮助我或者有人拥有源代码?
答案 0 :(得分:1)
您必须重新编码mp4文件,才能将滤镜应用于每一帧。我可以想到两种方法,但是它们需要高级的编程技能。我认为最简单的方法是FFMPEG(如果要重新编码,请确保检查许可证)。 This link可能会帮助您将其编译为Android。完成后,请查看FFMPEG文档和论坛以获取过滤器和叠加层。另一种(免费)方式是使用MediaCodec
重新编码视频,并使用GL着色器处理帧。 Grafika是一个项目,可以为您提供必要的工具。另外,互联网上可能会存在两种方式的预先构建的库,请确保首先使用给定的信息进行研究。
答案 1 :(得分:1)
您尝试过this one吗?它使用FFMPEG来添加滤镜/裁剪和更多编辑功能,这可以帮助您成为一个库,也可以给您一个想法,它具有一个使用此库构建的演示应用程序,play store here
答案 2 :(得分:1)
除FFMPEG方式外,我发现对自己有用的一种方式是使用GLSurfaceView。这个想法是在GLSurfaceView上渲染视频,并使用openGL渲染滤镜。检出this project。
答案 3 :(得分:1)
花了我一段时间,但我使用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 video filters corrupt mp4 file