1. 程式人生 > >android 獲取視訊縮圖終極解決方案(ffmpeg)

android 獲取視訊縮圖終極解決方案(ffmpeg)

前些天有個師弟(在做一個仿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

  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;
}

理論上,進過測試以上三個方案,基本的可以獲取網路或本地的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命令~

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

執行app,輸入框輸入:-
i /sdcard/test.asf -y -f image2 -t 0.001 -s 352x240 /sdcard/a.jpg
執行完畢,sdcard生成縮圖,棒!成功了!