2017年短视频应用如雨后春笋般先后上线,现在的短视频App大多支持本地视频的上传以及裁剪。下面讲一讲裁剪视频时预览视频图片的快速获取方法。当选择一个视频之后,底下通常有预览图片,这就是视频帧,比如快手上传本地视频的界面如图所示
获取视频帧的方式的有很多种,比如可以直接使用ffmpeg,也可以使用Android自带的MediaMetadataRetriever来获取指定时间的图片帧,当然,可以自己设置option来指定图片是否是关键帧,也会根据option设置的不同以不同的方式寻找关键帧(我测试的结果感觉返回的都是关键帧)。
MediaMetadataRetriever object = new MediaMetadataRetriever();
object.setDataSource(mPath);
//frameTime的单位为us微秒
object.getFrameAtTime(frameTime, MediaMetadataRetriever.OPTION_CLOSEST);
OPTION一共有四个,分别是OPTION_CLOSEST、OPTION_CLOSEST_SYNC、OPTION_PREVIOUS_SYNC和OPTION_NEXT_SYNC。这些参数应该是见字识意的,但是个人测试结果即使设置成OPTION_CLOSEST返回的数据仍然是关键帧。
使用此方法很简单,但是此方法的速度比较慢,尤其当视频文件较大时,根本达不到快手这类App点进去之后立马就能将图片显示出来的速度。所以肯定有更快的实现,但是一直没有想到一个特别好的思路,后来在bigflake.com/MediaCodec中看到了一个例子才算是找到一个更快的方法,通过MediaCodec进行解码,然后通过OpenGL渲染,最后通过glReadPixels来获取图片,每一步的时间都是ms级别,整个过程应该也不慢。
下面是官方的demo
//20131122: minor tweaks to saveFrame() I/O
//20131205: add alpha to EGLConfig (huge glReadPixels speedup); pre-allocate pixel buffers;
// log time to run saveFrame()
//20140123: correct error checks on glGet*Location() and program creation (they don't set error)
//20140212: eliminate byte swap
/**
* Extract frames from an MP4 using MediaExtractor, MediaCodec, and GLES. Put a .mp4 file
* in "/sdcard/source.mp4" and look for output files named "/sdcard/frame-XX.png".
* <p>
* This uses various features first available in Android "Jellybean" 4.1 (API 16).
* <p>
* (This was derived from bits and pieces of CTS tests, and is packaged as such, but is not
* currently part of CTS.)
*/
public class ExtractMpegFramesTest extends AndroidTestCase {
private static final String TAG = "ExtractMpegFramesTest";
private static final boolean VERBOSE = false; // lots of logging
// where to find files (note: requires WRITE_EXTERNAL_STORAGE permission)
private static final File FILES_DIR = Environment.getExternalStorageDirectory();
private static final String INPUT_FILE = "source.mp4";
private static final int MAX_FRAMES = 10; // stop extracting after this many
/** test entry point */
public void testExtractMpegFrames() throws Throwable {
ExtractMpegFramesWrapper.runTest(this);
}