使用 AndroidFrameConverter 将 javacv 帧转换为位图
Convert a javacv Frame Into Bitmap using AndroidFrameConverter
我正在尝试使用 javacv AndroidFrameConverter 将大小为 1280x720 的帧转换为位图,但转换需要很长时间。这是转换的示例代码。
FrameGrabber grabber = new FFmpegFrameGrabber(videoUrl);
frame = grabber.grab();
AndroidFrameConverter converter = new AndroidFrameConverter();
Bitmap originalBitmap = converter.convert(frame);
还有比这更快的解决方案吗?
FrameGrabber.grab()
返回的 Frame
是 javacv 特定的,Bitmap
是 Android 特定的,我怀疑两者之间是否有更快的转换方法,因为它们没有彼此了解并且都针对自己的使用进行了优化,他们无法优化两种数据格式之间的转换过程。
您正在移动 1280 * 720 * 4 字节,大约每帧 3.6 MB,这对于移动设备来说很多,尤其是考虑到内存分配。
我怀疑您是否会在不将 javacv 换成其他东西的情况下找到另一个转换工具 - 无法想象专门用于将 javacv 的 Frame
转换为 Android Bitmap
的工具来替换内置方法。
此外,AndroidFrameConverter.convert()
已经很聪明了 - 它只在第一次创建 Bitmap
或者如果规范发生变化(如大小等),然后只复制字节。
幸运的是,它有能力重用创建的 Bitmap
实例,FFmpegFrameGrabber
也有这种能力,所以在第一个 运行 他们将创建每个合适的对象,但是从第二帧应该重用它们拥有的对象——这样至少不会发生不必要的内存分配,这会降低移动设备的性能。这意味着平均转换许多帧应该比只转换一帧快得多——只是不要为每一帧创建新的抓取器和转换器实例。
使用 GPU 进行并行化可能是可能的 - 手动创建一个 Bitmap
,分区 Frame
的缓冲区并并行设置 Bitmap
的像素。我自己从来没有做过这样的事情,所以真的不能发表更多评论。请记住,并行算法有其自身的开销,并不总是比顺序算法更快。而且我不确定 GPU 编程的可移植性如何。使用 CPU 进行并行化不值得尝试 - 当前设备有多少个内核? 4?开销应该大于收益。
您可以使用此代码并根据需要更改过滤器。
public class FilterAsync extends AsyncTask {
private FFmpegFrameGrabber VIDEO_GRABBER;
private FFmpegFrameRecorder videoRecorder;
private File file = new File(Environment.getExternalStorageDirectory() + "/Download/Abc.mp4");
private Context mContext;
private FFmpegFrameFilter filter;
private boolean isTrue = false;
private ArrayList<String> videoPaths;
private File myDirectory;
public FilterAsync(Context context) {
mContext = context;
VIDEO_GRABBER = new FFmpegFrameGrabber(file);
myDirectory = new File(Environment.getExternalStorageDirectory() + "/Folder/");
if (!myDirectory.exists()) {
myDirectory.mkdirs();
}
videoPaths = new ArrayList<>();
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Object doInBackground(Object[] params) {
Frame tempVideoFrame;
try {
VIDEO_GRABBER.start();
initVideoRecorder(myDirectory + "/video" + System.currentTimeMillis() + ".mp4");
filter.start();
while (VIDEO_GRABBER.grab() != null) {
tempVideoFrame = VIDEO_GRABBER.grabImage();
if (tempVideoFrame != null) {
filter.push(tempVideoFrame);
tempVideoFrame = filter.pull();
videoRecorder.record(tempVideoFrame);
}
}
videoRecorder.stop();
filter.stop();
videoRecorder.release();
VIDEO_GRABBER.stop();
VIDEO_GRABBER.release();
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
} catch (FrameRecorder.Exception e) {
e.printStackTrace();
} catch (FrameFilter.Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
}
private void initVideoRecorder(String path) {
try {
filter = new FFmpegFrameFilter("transpose=clock ,crop=w=640:h=480:x=0:y=0", VIDEO_GRABBER.getImageWidth(), VIDEO_GRABBER.getImageHeight());
videoRecorder = FFmpegFrameRecorder.createDefault(path, VIDEO_GRABBER.getImageWidth(), VIDEO_GRABBER.getImageHeight());
videoRecorder.setAudioChannels(VIDEO_GRABBER.getAudioChannels());
videoRecorder.start();
} catch (FrameRecorder.Exception e) {
e.printStackTrace();
}
}
}
我正在尝试使用 javacv AndroidFrameConverter 将大小为 1280x720 的帧转换为位图,但转换需要很长时间。这是转换的示例代码。
FrameGrabber grabber = new FFmpegFrameGrabber(videoUrl);
frame = grabber.grab();
AndroidFrameConverter converter = new AndroidFrameConverter();
Bitmap originalBitmap = converter.convert(frame);
还有比这更快的解决方案吗?
FrameGrabber.grab()
返回的 Frame
是 javacv 特定的,Bitmap
是 Android 特定的,我怀疑两者之间是否有更快的转换方法,因为它们没有彼此了解并且都针对自己的使用进行了优化,他们无法优化两种数据格式之间的转换过程。
您正在移动 1280 * 720 * 4 字节,大约每帧 3.6 MB,这对于移动设备来说很多,尤其是考虑到内存分配。
我怀疑您是否会在不将 javacv 换成其他东西的情况下找到另一个转换工具 - 无法想象专门用于将 javacv 的 Frame
转换为 Android Bitmap
的工具来替换内置方法。
此外,AndroidFrameConverter.convert()
已经很聪明了 - 它只在第一次创建 Bitmap
或者如果规范发生变化(如大小等),然后只复制字节。
幸运的是,它有能力重用创建的 Bitmap
实例,FFmpegFrameGrabber
也有这种能力,所以在第一个 运行 他们将创建每个合适的对象,但是从第二帧应该重用它们拥有的对象——这样至少不会发生不必要的内存分配,这会降低移动设备的性能。这意味着平均转换许多帧应该比只转换一帧快得多——只是不要为每一帧创建新的抓取器和转换器实例。
使用 GPU 进行并行化可能是可能的 - 手动创建一个 Bitmap
,分区 Frame
的缓冲区并并行设置 Bitmap
的像素。我自己从来没有做过这样的事情,所以真的不能发表更多评论。请记住,并行算法有其自身的开销,并不总是比顺序算法更快。而且我不确定 GPU 编程的可移植性如何。使用 CPU 进行并行化不值得尝试 - 当前设备有多少个内核? 4?开销应该大于收益。
您可以使用此代码并根据需要更改过滤器。
public class FilterAsync extends AsyncTask {
private FFmpegFrameGrabber VIDEO_GRABBER;
private FFmpegFrameRecorder videoRecorder;
private File file = new File(Environment.getExternalStorageDirectory() + "/Download/Abc.mp4");
private Context mContext;
private FFmpegFrameFilter filter;
private boolean isTrue = false;
private ArrayList<String> videoPaths;
private File myDirectory;
public FilterAsync(Context context) {
mContext = context;
VIDEO_GRABBER = new FFmpegFrameGrabber(file);
myDirectory = new File(Environment.getExternalStorageDirectory() + "/Folder/");
if (!myDirectory.exists()) {
myDirectory.mkdirs();
}
videoPaths = new ArrayList<>();
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Object doInBackground(Object[] params) {
Frame tempVideoFrame;
try {
VIDEO_GRABBER.start();
initVideoRecorder(myDirectory + "/video" + System.currentTimeMillis() + ".mp4");
filter.start();
while (VIDEO_GRABBER.grab() != null) {
tempVideoFrame = VIDEO_GRABBER.grabImage();
if (tempVideoFrame != null) {
filter.push(tempVideoFrame);
tempVideoFrame = filter.pull();
videoRecorder.record(tempVideoFrame);
}
}
videoRecorder.stop();
filter.stop();
videoRecorder.release();
VIDEO_GRABBER.stop();
VIDEO_GRABBER.release();
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
} catch (FrameRecorder.Exception e) {
e.printStackTrace();
} catch (FrameFilter.Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
}
private void initVideoRecorder(String path) {
try {
filter = new FFmpegFrameFilter("transpose=clock ,crop=w=640:h=480:x=0:y=0", VIDEO_GRABBER.getImageWidth(), VIDEO_GRABBER.getImageHeight());
videoRecorder = FFmpegFrameRecorder.createDefault(path, VIDEO_GRABBER.getImageWidth(), VIDEO_GRABBER.getImageHeight());
videoRecorder.setAudioChannels(VIDEO_GRABBER.getAudioChannels());
videoRecorder.start();
} catch (FrameRecorder.Exception e) {
e.printStackTrace();
}
}
}