安卓视频帧捕获

7

我需要获取视频文件的第一帧(它可以在sdcard、缓存目录或应用程序目录中)。 我的应用程序中有android.media包,其中包含MediaMetadataRetriever类。 为了将第一帧提取到位图中,我使用以下代码:

public static Bitmap getVideoFrame(Context context, Uri videoUri) {
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    try {
        retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
        retriever.setDataSource(context, videoUri);
        return retriever.captureFrame();
    } catch (IllegalArgumentException ex) {
        throw new RuntimeException();
    } catch (RuntimeException ex) {
        throw new RuntimeException();
    } finally {
        retriever.release();
    }
}

但是这不起作用。当我设置数据源时,它会抛出异常(java.lang.RuntimeException:setDataSource失败:状态= 0x80000000)。您知道如何使此代码工作吗?或者,您是否有任何类似的(简单)解决方案,而无需使用ffmpeg或其他外部库?videoUri是有效的URI(媒体播放器可以从该URI播放视频)。

你能提供一个问题的样例吗?我尝试了很多这种类型的样例,最终不得不向你询问。 - ManishSB
8个回答

13

以下对我有效:

public static Bitmap getVideoFrame(FileDescriptor FD) {
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            retriever.setDataSource(FD);
            return retriever.getFrameAtTime();
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        } catch (RuntimeException ex) {
            ex.printStackTrace();
        } finally {
            try {
                retriever.release();
            } catch (RuntimeException ex) {
            }
        }
        return null;
    }

如果使用路径而不是文件描述符,也可以正常工作。


作为FileDescripter参数应该传递什么值?实际情况是,在我的应用程序中,只有视频网址,并且想要获取该视频文件的帧列表... - Ashish Dwivedi
要获取一个FileDescriptor对象,请执行 - openFileInput(myVideoFileName).getFD()。为此,视频必须位于您的应用程序私有目录中。您还可以更改为public static Bitmap getVideoFrame(String FD),并传递您的视频路径。 - Rab Ross
一个视频每秒有25到30帧。你能获取所有的帧吗?因为使用这种技术,我每次得到3-4个重复的帧,而且有些帧会丢失。 - Akanksha Rathore
1
@Rab Ross,请提供详细的代码。我无法使其正常工作。扩展名应该是什么,反之亦然。 - Zar E Ahmer
@Nepster 这是一段时间前的事情了,我现在无法访问详细的代码了。非常抱歉。 - Rab Ross

6

试试这个,我用过它,它可行

public static Bitmap getVideoFrame(Context context, Uri uri) {
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    try {
        retriever.setDataSource(uri.toString(),new HashMap<String, String>());
        return retriever.getFrameAtTime();
    } catch (IllegalArgumentException ex) {
        ex.printStackTrace();
    } catch (RuntimeException ex) {
        ex.printStackTrace();
    } finally {
        try {
            retriever.release();
        } catch (RuntimeException ex) {
        }
    }
    return null;
}

在这里,您可以直接传递您的URL,而无需使用URI。

需要API 14才能使用。如何在低于此版本的API中使用它? - Suraj
你可以使用这个FFmpegMediaMetadataRetriever https://github.com/wseemann/FFmpegMediaMetadataRetriever。 - Arul Pandian

1

我使用了这段代码,它对我很有效。你可以试试这个。

if (Build.VERSION.SDK_INT >= 14) {
                ffmpegMetaDataRetriever.setDataSource(
                        videoFile.getAbsolutePath(),
                        new HashMap<String, String>());
            } else {
                ffmpegMetaDataRetriever.setDataSource(videoFile
                        .getAbsolutePath());
            }

0

我在使用ThumbnailUtilshttp://developer.android.com/reference/android/media/ThumbnailUtils.html时遇到了相同的错误。
它在底层使用MediaMetadataRetriever,大多数情况下,您可以使用此方法发送文件路径而没有问题:

public static Bitmap createVideoThumbnail (String filePath, int kind)  

然而,在Android 4.0.4上,我一直遇到与@gabi相同的错误。使用文件描述符解决了问题,并且对于非4.0.4设备仍然有效。实际上,我最终创建了ThumbnailUtils的子类。这是我的子类方法:

 public static Bitmap createVideoThumbnail(FileDescriptor fDescriptor, int kind) 
 {
    Bitmap bitmap = null;
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    try {
        retriever.setDataSource(fDescriptor);
        bitmap = retriever.getFrameAtTime(-1);
    } 
    catch (IllegalArgumentException ex) {
        // Assume this is a corrupt video file
        Log.e(LOG_TAG, "Failed to create video thumbnail for file description: " + fDescriptor.toString());
    }
    catch (RuntimeException ex) {
        // Assume this is a corrupt video file.
        Log.e(LOG_TAG, "Failed to create video thumbnail for file description: " + fDescriptor.toString());
    } finally {
        try {
            retriever.release();
        } catch (RuntimeException ex) {
            // Ignore failures while cleaning up.
        }
    }

    if (bitmap == null) return null;

    if (kind == Images.Thumbnails.MINI_KIND) {
        // Scale down the bitmap if it's too large.
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int max = Math.max(width, height);
        if (max > 512) {
            float scale = 512f / max;
            int w = Math.round(scale * width);
            int h = Math.round(scale * height);
            bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
        }
    } else if (kind == Images.Thumbnails.MICRO_KIND) {
        bitmap = extractThumbnail(bitmap,
                TARGET_SIZE_MICRO_THUMBNAIL,
                TARGET_SIZE_MICRO_THUMBNAIL,
                OPTIONS_RECYCLE_INPUT);
    }
    return bitmap;
}

0

我的应用程序也有同样的错误。我在这个网站上看到,

这是一种非官方的解决方法, 它只能在杯子蛋糕版本(和 可能是后来的版本)中工作。Android团队 不能保证 libmedia_jni.so,即java文件所使用的库, 在未来版本中是否包含或具有相同的接口。

http://osdir.com/ml/AndroidDevelopers/2009-06/msg02442.html

我已经将手机升级到GingerBread,但它不再工作。


0

File不存在时,也会抛出异常。因此,在调用setDataSource()之前,最好检查一下new File(url).exists()


0

Uri并不是非常具体的。有时它们指的是捆绑包中的某些内容。它们经常需要被转换为绝对路径形式。在您使用Uri的另一种情况下,它可能已经足够聪明地检查了Uri的类型。但是,您展示的这种情况似乎没有仔细查看。


-2

那么,从视频中获取帧的特定方法是什么?

 File sdcard = Environment.getExternalStorageDirectory();
 File file = new File(sdcard, "myvideo.mp4");

它一直在报错...现在它可以工作了...我猜我的Eclipse编辑器有些问题。无论如何,还是谢谢。 - Dhiraj Tayade
我的评论是问你,“这两行代码是你的答案还是带有你问题的疑问?” - Paresh Mayani
这是一个问题...上面的代码最初没有起作用...但现在它是正确的,正在运行。 - Dhiraj Tayade
@PareshMayani,请看一下我的问题,如何获取视频文件的帧列表以在画廊视图中显示。 - Ashish Dwivedi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接