从视频中获取多个缩略图

Get multiple thumbnails from video

我正在使用 MediaMetadataRetriever 检索视频中特定时间的缩略图。这就是我实现这一目标的方式:

MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
    try {
        metadataRetriever.setDataSource(MainActivity.this, Uri.parse("android.resource://packageName/raw/"+"test"));
        String duration=metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
        long time = Long.valueOf(duration)/3;
        Bitmap bitmap1 = metadataRetriever.getFrameAtTime(time,MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
        imgone.setImageBitmap(bitmap1);

    }catch (Exception ex) {
        Toast.makeText(MainActivity.this, String.valueOf(ex), Toast.LENGTH_SHORT).show();
        }

这个 returns 一个 bitmap/thumbnail 和预期的一样,问题是如果我想在视频中的不同时间获得多个缩略图,就像这样:

MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
    try {
        metadataRetriever.setDataSource(MainActivity.this, Uri.parse("android.resource://packageName/raw/"+"test"));
        String duration=metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
        long time = Long.valueOf(duration)/3;
        long time2 = time+time;
        long time3 = time+time+time;
        Bitmap bitmap1 = metadataRetriever.getFrameAtTime(time,MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
        Bitmap bitmap2 = metadataRetriever.getFrameAtTime(time2,MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
        Bitmap bitmap3 = metadataRetriever.getFrameAtTime(time3,MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
        imgone.setImageBitmap(bitmap1);
        imgtwo.setImageBitmap(bitmap2);
        imgthree.setImageBitmap(bitmap3);

    }catch (Exception ex) {
        Toast.makeText(MainActivity.this, String.valueOf(ex), Toast.LENGTH_SHORT).show();
        }

然后它仍然只有 returns 相同的缩略图,我不确定是因为视频只有一个缩略图还是什么,但我已经尝试过不同的视频文件具有相同的缩略图结果。

我已尝试将 MediaMetadataRetriever.OPTION_CLOSEST_SYNC 更改为所有可用选项,但结果仍然相同。

我不确定 FFMPEG 是否是更好的选择?

试试这个

 public void detectBitmapFromVideo(int secondcount, int framecount, String videoPath) {
        //int fps = 800000 / framecount;
        int delta_time = secondcount * 1000000; //in microsecs
        //FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
        //mmr.setDataSource(videoPath);
        //String s_duration = mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION);
        MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
        mediaMetadataRetriever.setDataSource(videoPath);
        int duration = getVideoDuration(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
        //int duration = getVideoDuration(s_duration);
        ArrayList<Frame> frames = new ArrayList<Frame>();
        //Log.e("Duration ", "Duration  = " + duration + " Delta time = " + delta_time);
        for (int i = 0; i <= duration; i += delta_time) {
            Bitmap bmFrame = mediaMetadataRetriever.getFrameAtTime(i);
            //unit in microsecond
            if (bmFrame == null) {
                //Log.e(TAG, "frame image " + bmFrame.toString());
                continue;
            }
            //saveBitmapImage(bmFrame,i+"");
            frames.add(new Frame.Builder().setBitmap(bmFrame).build());



            /*Bitmap frame_orig = mmr.getFrameAtTime(i, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
            if (frame_orig == null) {
                continue;
            }

            frames.add(new Frame.Builder().setBitmap(rotateBitmap(frame_orig, 90f)).build());
            //Log.e("Faces Detected", "Face detection on going  duration = " + duration + " Deleta time = " + i);
        }

    }

整整一年后,我发现我从未提供过答案。

在最初的问题中我想检索 3 个缩略图,我最终检索到 5 个。我还提到我不确定 FFmpeg 是否是一个合适的选项,这正是我使用的。

因此,在 OnCreate 中,我确保支持 FFmpeg,然后执行以下操作:

if (FFmpeg.getInstance(getApplication()).isSupported()) {
      @SuppressLint("SimpleDateFormat")
      //ffmpeg expects the time format to be "00:00:00"
      Format formatter = new SimpleDateFormat("00:" + "mm:ss.SS");
      //Get the duration of the video
      long duration = player.getDuration();
      //Since I want 5 thumbnails, I divide the duration by 6 to get the first thumbnail position 
      long img1 = duration / 6;
      //I format the first thumbnail time since ffmpeg expects "00:00:00" format
      String firstTumbTime = formatter.format(img1);

      //Scale the size of the thumbnail output (this can be improved/changed to your liking)
      String scaledSize = displayMetrics.widthPixels / 7 + ":" + displayMetrics.heightPixels / 7;
      //Set ffmpeg command (notice that I set vframes to one, since I only want 1 thumbnail/image)
      String[] a = {"-ss", firstTumbTime, "-i", mStringFilePath, "-vframes", "1", "-s", scaledSize, imageThumbsDirectory + "/" + "thumb1.bmp"};
      //start ffmpeg asynctask for the first thumbnail
      ExecuteThumbFFMPEG(a);

}  else {
     Toast.makeText(TestNewPlayer.this, "Your device doesn't support FFMPEG...", Toast.LENGTH_SHORT).show();
}

上面代码中的注释说明了一切,下面是我的ExecuteThumbFFMPEG方法。

public void ExecuteThumbFFMPEG(String[] command) {

    ffmpegImages = FFmpeg.getInstance(this).execute(command, new ExecuteBinaryResponseHandler() {

        @Override
        public void onStart() {
            //ffmpeg started
        }

        @Override
        public void onProgress(String message) {
            //get ffmpeg progress
        }

        @Override
        public void onFailure(String message) {
            //ffmpeg failed
        }

        @Override
        public void onSuccess(String message) {
            //first thumbnail saved successfully, now to get the other 4

            //Scale the thumbnail output (Same as above)
            String scaledSize = displayMetrics.widthPixels / 7 + ":" + displayMetrics.heightPixels / 7;

            try {
                //I first set the path/name for each thumbnail, this will also be used to check if the thumbnail is available or if we should get it
                String imgPath1 = imageThumbsDirectory + "/" + "thumb1.bmp";
                String imgPath2 = imageThumbsDirectory + "/" + "thumb2.bmp";
                String imgPath3 = imageThumbsDirectory + "/" + "thumb3.bmp";
                String imgPath4 = imageThumbsDirectory + "/" + "thumb4.bmp";
                String imgPath5 = imageThumbsDirectory + "/" + "thumb5.bmp";

                //Set the format again (same as above)
                @SuppressLint("SimpleDateFormat")
                Format formatter = new SimpleDateFormat("00:" + "mm:ss.SS");

                //Get the length of the video
                long duration = Player.getDuration();

                //Divide the length of the video by 6 (same as above)
                long time = duration / 6;

                //Since I want 5 thumbnails evenly distributed throughout the video
                //I use the video length divided by 6 to accomplish that
                long img2 = time + time;
                long img3 = time + time + time;
                long img4 = time + time + time + time;
                long img5 = time + time + time + time + time;

                //Format the time (calculated above) for each thumbnail I want to retrieve
                String Img2Timeformat = formatter.format(img2);
                String Img3Timeformat = formatter.format(img3);
                String Img4Timeformat = formatter.format(img4);
                String Img5Timeformat = formatter.format(img5);

                //Get reference to the thumbnails (to see if they have been created before)
                File fileimgPath1 = new File(imgPath1);
                File fileimgPath2 = new File(imgPath2);
                File fileimgPath3 = new File(imgPath3);
                File fileimgPath4 = new File(imgPath4);
                File fileimgPath5 = new File(imgPath5);

                //If thumbnail 1 exist and thumbnail 2 doesn't then we need to get thumbnail 2
                if (fileimgPath1.exists() && !fileimgPath2.exists()) {
                    //Get/decode bitmap from the first thumbnail path to be able to set it to our ImageView that should hold the first thumbnail
                    Bitmap bmp1 = BitmapFactory.decodeFile(imgPath1);
                    //Set the first thumbnail to our first ImageView
                    imgone.setImageBitmap(bmp1);
                    //Set the ffmpeg command to retrieve the second thumbnail
                    String[] ffmpegCommandForThumb2 = {"-ss", Img2Timeformat, "-i", mStringFilePath, "-vframes", "1", "-s", scaledSize, imageThumbsDirectory + "/" + "thumb2.bmp"};
                    //Start ffmpeg again, this time we will be getting thumbnail 2
                    ExecuteThumbFFMPEG(ffmpegCommandForThumb2);
                }

                //If thumbnail 2 exist and thumbnail 3 doesn't then we need to get thumbnail 3
                if (fileimgPath2.exists() && !fileimgPath3.exists()) {
                    //Get/decode bitmap from the second thumbnail path to be able to set it to our ImageView that should hold the second thumbnail
                    Bitmap bmp2 = BitmapFactory.decodeFile(imgPath2);
                    //Set the second thumbnail to our second ImageView
                    imgTwo.setImageBitmap(bmp2);
                    //Set the ffmpeg command to retrieve the third thumbnail
                    String[] ffmpegCommandForThumb3 = {"-ss", Img3Timeformat, "-i", mStringFilePath, "-vframes", "1", "-s", scaledSize, imageThumbsDirectory + "/" + "thumb3.bmp"};
                    //Start ffmpeg again, this time we will be getting thumbnail 3
                    ExecuteThumbFFMPEG(ffmpegCommandForThumb3);
                }

                ////If thumbnail 3 exist and thumbnail 4 doesn't then we need to get thumbnail 4
                if (fileimgPath3.exists() && !fileimgPath4.exists()) {
                    //Get/decode bitmap from the third thumbnail path to be able to set it to our ImageView that should hold the third thumbnail
                    Bitmap bmp3 = BitmapFactory.decodeFile(imgPath3);
                    //Set the third thumbnail to our third ImageView
                    imgThree.setImageBitmap(bmp3);
                    //Set the ffmpeg command to retrieve the fourth thumbnail
                    String[] ffmpegCommandForThumb4 = {"-ss", Img4Timeformat, "-i", mStringFilePath, "-vframes", "1", "-s", scaledSize, imageThumbsDirectory + "/" + "thumb4.bmp"};
                    //Start ffmpeg again, this time we will be getting thumbnail 4
                    ExecuteThumbFFMPEG(ffmpegCommandForThumb4);
                }

                ////If thumbnail 4 exist and thumbnail 5 doesn't then we need to get thumbnail 5
                if (fileimgPath4.exists() && !fileimgPath5.exists()) {
                    //Get/decode bitmap from the first fourth path to be able to set it to our ImageView that should hold the fourth thumbnail
                    Bitmap bmp4 = BitmapFactory.decodeFile(imgPath4);
                    //Set the fourth thumbnail to our fourth ImageView
                    imgFour.setImageBitmap(bmp4);
                    //Set the ffmpeg command to retrieve the last thumbnail
                    String[] ffmpegCommandForThumb5 = {"-ss", Img5Timeformat, "-i", mStringFilePath, "-vframes", "1", "-s", scaledSize, imageThumbsDirectory + "/" + "thumb5.bmp"};
                    //Start ffmpeg again, this time we will be getting thumbnail 5
                    ExecuteThumbFFMPEG(ffmpegCommandForThumb5);
                }

                //If thumbnail 5 exist, then we are done and we need to set it to our ImageView
                if (fileimgPath5.exists()) {
                    Bitmap bmp5 = BitmapFactory.decodeFile(imgPath5);
                    imgFive.setImageBitmap(bmp5);
                }


            } catch (Exception ex) {
                Toast.makeText(Player.this, String.valueOf(ex), Toast.LENGTH_SHORT).show();
            }

        }

        @Override
        public void onFinish() {
            //ffmpeg is done
        }


    });

}

当用户退出 ActivityOnDestroy 被调用时,所有的缩略图都应该被删除,我通过调用以下方法来做到这一点:

DeleteThumbs.deleteAllThumbnails(getBaseContext());

这里是DeleteThumbsclass删除所有thumbnails/images

class DeleteThumbs {

    @SuppressWarnings("unused")
    static void deleteAllThumbnails(Context baseContext){
        //Directory where all the thumbnails are stored
        File imageThumbsDirectory = baseContext.getExternalFilesDir("ThumbTemp");
        //Path to each thumbnail
        File f1 = new File(imageThumbsDirectory + "/" + "thumb1.bmp");
        File f2 = new File(imageThumbsDirectory + "/" + "thumb2.bmp");
        File f3 = new File(imageThumbsDirectory + "/" + "thumb3.bmp");
        File f4 = new File(imageThumbsDirectory + "/" + "thumb4.bmp");
        File f5 = new File(imageThumbsDirectory + "/" + "thumb5.bmp");

        boolean d1 = f1.delete();
        boolean d2 = f2.delete();
        boolean d3 = f3.delete();
        boolean d4 = f4.delete();
        boolean d5 = f5.delete();
    }

}

因为我们知道每个缩略图的名称,所以一次删除它们很容易。

这为我提供了 5 个缩略图图像,这些图像经过缩放以减少加载到 ImageView 中的时间。因为我将视频的时长除以 6,所以我得到 5 张图像,它们在整个视频中均匀 "distributed"。

注意:

这可以通过将图像缓存到内存中或使用像 picasso 或 glide 这样的库来为我们处理图像加载来改进。