从android中的视频中提取位图
Posted
技术标签:
【中文标题】从android中的视频中提取位图【英文标题】:Extract bitmap from video in android 【发布时间】:2013-02-27 14:08:43 【问题描述】:我正在尝试从视频中提取所有帧。 通过以下代码,我想获取视频的前 30 帧,但我只得到了第一帧 30 次。
private ArrayList<Bitmap> getFrames(String path)
try
ArrayList<Bitmap> bArray = new ArrayList<Bitmap>();
bArray.clear();
MediaMetadataRetriever mRetriever = new MediaMetadataRetriever();
mRetriever.setDataSource("/sdcard/myvideo.mp4");
for (int i = 0; i < 30; i++)
bArray.add(mRetriever.getFrameAtTime(1000*i,
MediaMetadataRetriever.OPTION_CLOSEST_SYNC));
return bArray;
catch (Exception e) return null;
现在,如何获取视频中的所有帧?
【问题讨论】:
getFrameAtTime
的时间以微秒为单位,因此对于 30 fps 的视频,每帧之间大约有 33333 微秒。您的代码尝试读取的最后一帧是 30000 微秒 - 即您甚至不会前进到第二帧(当然取决于您的帧速率)。另一件事是OPTION_CLOSEST_SYNC
检索最接近您指定时间的关键帧。压缩视频中的关键帧通常少于总帧数。
你好@Michael,我的视频是 30 fps。现在我正在使用以下代码提取位图 .... for (int i = 0; i
正如我在第一条评论中所写,当您使用OPTION_CLOSEST_SYNC
时,您正在检索最接近给定时间的关键帧。对于每个关键帧,视频包含 10 帧或更多帧的可能性不大。如果您想获得任何类型的帧而不仅仅是关键帧,请使用 OPTION_CLOSEST
。
我使用了 OPTION_CLOSEST .. 但我的视频长度只有 2 秒。
OPTION_CLOSEST
是您应该使用的,如果您想获得任何帧(而不仅仅是关键帧),无论视频的长度如何。
【参考方案1】:
android SDK 中的视频支持有限,只能对关键帧进行 H264 编码视频的帧提取。为了提取任意帧,您需要使用像 FFmpegMediaMetadataRetriever 这样的库,它使用本机代码从视频中提取数据。它速度非常快,附带预编译的二进制文件(用于 ARM 和 x86),因此您无需深入研究 C++ 和 makefile,在 Apache 2.0 下获得许可,并附带一个演示 Android 应用程序。
还有一个纯 Java 库,JCodec,但速度较慢,而且当我去年使用它时,提取帧的颜色会失真。
【讨论】:
我尝试了 JCodec,但除了 240fps 慢动作 mp4 的关键帧外,我什么也得不到。我最终使用了 opencv 的 Java 绑定,即 VideoCapture 类。我相信它在幕后使用 ffmpeg。我能够获得所有帧,以及每个名人的时间戳和视频的总帧数。警告 - 这是 java sdk,我还没有尝试过最新的适用于 android 的 opencv。【参考方案2】:您必须将路径传递给此方法...完美运行的代码!希望对你有帮助
gradle-- 实现 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.15'
public void VideoToGif(String uri)
Uri videoFileUri = Uri.parse(uri);
FFmpegMediaMetadataRetriever retriever = new FFmpegMediaMetadataRetriever();
retriever.setDataSource(uri);
List<Bitmap> rev = new ArrayList<Bitmap>();
MediaPlayer mp = MediaPlayer.create(GitToImage.this, videoFileUri);
int millis = mp.getDuration();
System.out.println("starting point");
for (int i = 100000; i <=millis * 1000; i += 100000*2)
Bitmap bitmap = retriever.getFrameAtTime(i, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
rev.add(bitmap);
GiftoImage((ArrayList) rev);
【讨论】:
【参考方案3】:getFrameAt 以毫秒为单位获取数据,但您在 for 循环中增加了 0.001 毫秒。
for(int i=1000000;i<millis*1000;i+=1000000) // for incrementing 1s use 1000
bArray.add(mRetriever.getFrameAtTime(i,
MediaMetadataRetriever.OPTION_CLOSEST_SYNC));
像上面一样改变它。以上是创建您想要的示例。我也回复了here
【讨论】:
【参考方案4】:从 Android 9.0(API 级别 28)开始,MediaMetadataRetriever
有一个 getFrameAtIndex (int frameIndex)
方法,它接受您想要的帧的从零开始的索引并返回一个位图。
见https://developer.android.com/reference/android/media/MediaMetadataRetriever.html#getFrameAtIndex(int)
【讨论】:
以上是关于从android中的视频中提取位图的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Zxing 应用程序在 android 中扫描位图?