GeorgeYang'Blog

my technology blog

android 获取视频缩略图终极解决方案(ffmpeg)

阅读:1520 创建时间:15-12-27 00:36:24 tags:ffmpeg,android,java

前些天有个师弟(在做一个仿LinkInEyes行车记录仪的app)问我怎么获取视频缩略图,起初以为很简单,就找了个常用的解决方案(用户获取正常的视频文件的缩略图):

  1. 方案1:

    private void initView() { imgPic = (ImageView) findViewById(R.id.img_pic); seekbar = (SeekBar) findViewById(R.id.seekbar); mmr = new MediaMetadataRetriever(); mmr.setDataSource("/sdcard/3.mp4"); // 取得视频的长度(单位为毫秒) String time = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); seekbar.setMax(Integer.parseInt(time) * 1000); bt = (Button) findViewById(R.id.bt); bt.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, " " + seekbar.getProgress(), 1).show();

         Bitmap bitmap = mmr.getFrameAtTime(seekbar.getProgress(),
                         MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
         // Bitmap bitmap = mmr.getFrameAtTime(seekbar.getProgress());
         imgPic.setImageBitmap(bitmap);
     System.out.println(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE)+ "");
     System.out.println(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE)+ "");
    

    // mmr.release(); } }); }

要求android api>=10

  1. 方案2:

ThumbnailUtils.createVideoThumbnail(filePath, kind);方法获取

  public static Bitmap createVideoThumbnail(String filePath, int kind) {
         Bitmap bitmap = null;
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         try {
             retriever.setDataSource(filePath);
             bitmap = retriever.getFrameAtTime(-1);
         } catch (IllegalArgumentException ex) {
             // Assume this is a corrupt video file
         } catch (RuntimeException ex) {
             // Assume this is a corrupt video file.
         } 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;
     }

该方法在2.x系统下可用,API LEVEL > 14时却只能返回null

http://my.oschina.net/kylinhuang/blog/472757?fromerr=vASTONAf

  1. 方案3

使用开源代码:MediaMetadataRetriever

 private Bitmap getBitMap() {
     Bitmap b = null;
     // Retrieve all metadata.
     List<Metadata> metadata = new ArrayList<Metadata>();

     if (mUri == null) return null;
     FFmpegMediaMetadataRetriever fmmr = new FFmpegMediaMetadataRetriever();
     try {
         fmmr.setDataSource(mUri);
         for (int i = 0; i < Constants.METADATA_KEYS.length; i++) { //注意
             String key = Constants.METADATA_KEYS[i];
             String value = fmmr.extractMetadata(key);

             if (value != null) {
                 metadata.add(new Metadata(key, value));
                 Log.i(MetadataLoader.class.getName(), "Key: " + key
                         + " Value: " + value);
             }
         }
         b = fmmr.getFrameAtTime();
         if (b != null) {
             Bitmap b2 = fmmr.getFrameAtTime(seekbar.getProgress(),
                     FFmpegMediaMetadataRetriever.OPTION_CLOSEST_SYNC);
             if (b2 != null) {
                 b = b2;
             }
         }

         if (b != null) {
             metadata.add(new Metadata("image", b));
             Log.i(MetadataLoader.class.getName(), "Extracted frame");
         } else {
             Log.e(MetadataLoader.class.getName(), "Failed to extract frame");
         }
     } catch (IllegalArgumentException ex) {
         ex.printStackTrace();
     } finally {
         fmmr.release();
     }
     return b;
 }

fmmr.setDataSource(mUri);中uri支持的文件类型 file, http, https, mms and mmsh 支持的编码格式(音频&视频): aac, acc+, avi, flac, mp2, mp3, mp4, ogg, 3gp and more! 扩展支持: 开源地址:https://github.com/wseemann/FFmpegMediaMetadataRetriever 備份地址:https://github.com/qq179157977/FFmpegMediaMetadataRetriever

理论上,进过测试以上三个方案,基本的可以获取网络或本地的map4,avi等视频的缩略图了。 但是师弟说还是获取不了,因为他获取到的是一个thm文件(idr流返回),这种文件不是视频文件,是是MP4或者MPG视频文件的索引文件,用以上三种方法根本获取不到缩略图,我就觉得奇怪,想起强大的ffmpeg也可以获取缩略图,于是下载了一个命令行的ffmpeg开始尝试获取缩略图: 先将文件放到c盘,切换到c盘(ffmpeg也在c盘),运行: ffmpeg -i C:\fengkai.htm -y -f image2 -t 0.001 -s 352x240 a.jpg 运行完毕,出现了a.jpg,还真的可以获取htm文件的缩略图

但是,ffmpeg命令要在android上运行,需要将ffmpeg(linux版)文件放到一个目录,然后用root权限给予ffmpeg执行权,才可以调用上述获取缩略图命令,原因是系统没有ffmpeg命令~

这是只能继续找到不需要权限又能通过ffmpeg获取缩略图的demo了,找了一段时间,找到一个外国人写的牛逼开源项目:

https://github.com/WritingMinds/ffmpeg-android-java

http://i.imgur.com/cP4WhLn.gif

下载测试app

运行app,输入框输入:- i /sdcard/test.asf -y -f image2 -t 0.001 -s 352x240 /sdcard/a.jpg 执行完毕,sdcard生成缩略图,棒!成功了!

附录 thm测试文件

参考: http://my.oschina.net/kylinhuang/blog/472757?fromerr=vASTONAf http://www.tuicool.com/articles/uqmUFfY http://helloandroid.iteye.com/blog/1753355