从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 中扫描位图?

如何从 android 中的 sdcard 图像创建位图?

如何从 Android 中的大位图加载图块?

Android:从图库加载的位图在 ImageView 中旋转

Android 提取解码编码多路复用音频

Android:从位图转换为 Byte[] OutOfMemory 错误