9

I am trying to extract all frames from a video.
By following code I want to fetch the first 30 frames of a video, but I got only first frame 30 times.

private ArrayList<Bitmap> getFrames(String path) {
    try {
        ArrayList<Bitmap> bArray = new ArrayList<Bitmap>();
        bArray.clear();
        MediaMetadataRetriever mRetriever = new MediaMetadataRetriever();
        mRetriever.setDataSource("/sdcard/myvideo.mp4");                    

        for (int i = 0; i < 30; i++) {
            bArray.add(mRetriever.getFrameAtTime(1000*i, 
                       MediaMetadataRetriever.OPTION_CLOSEST_SYNC));
        }
        return bArray;
    } catch (Exception e) { return null; }
}

Now, how can I get all frames from a video?

quetzalcoatl
  • 30,247
  • 8
  • 62
  • 100
krishna kant gupta
  • 121
  • 1
  • 1
  • 6
  • The time given to `getFrameAtTime` is in microseconds, so for a 30 fps video there will be approximately 33333 microseconds between each frame. The last frame your code tries to read is at 30000 microseconds - i.e. you won't even have moved ahead to the second frame (depending on your frame rate of course). The other thing is that `OPTION_CLOSEST_SYNC` retrieves the _keyframe_ closest to the time you specify. There are typically less keyframes than total frames in a compressed video. – Michael Feb 27 '13 at 15:17
  • Hello @Michael, My Video with 30 fps. and Now i am using following code for extract bitmaps .... for (int i = 0; i < 10; i++) { bArray.add(mRetriever.getFrameAtTime(33333*i, MediaMetadataRetriever.OPTION_CLOSEST_SYNC)); } but nothing be change.. here i got only first frame in bitmap.. – krishna kant gupta Feb 28 '13 at 08:24
  • 1
    As I wrote in my first comment, you're retrieving the keyframe closest to the given time when you use `OPTION_CLOSEST_SYNC`. It's not unlikely that the video contains 10 or more frames for every keyframe. Use `OPTION_CLOSEST` if you want to get any kind of frame instead of just keyframes. – Michael Feb 28 '13 at 08:33
  • i used OPTION_CLOSEST .. but length of my video is only 2 second. – krishna kant gupta Feb 28 '13 at 10:07
  • `OPTION_CLOSEST` is what you should use if you want to get any frame (rather than just the keyframes) regardless of the length of the video. – Michael Feb 28 '13 at 10:25
  • 33333 is not working.. so i used 3333333. and OPTION_CLOSEST.. by this code.. i got first 3-4 frame are same.. but after this i got next frame with 4-5 times.. after it first frame repeat every time. – krishna kant gupta Feb 28 '13 at 10:43
  • have u get the solution, so please reply http://stackoverflow.com/questions/22684347/extract-all-video-frames-in-android. I am stuck in same issue. – Akanksha Rathore Mar 28 '14 at 07:50

4 Answers4

3

Video support in Android SDK is limited and frame extraction for H264 encoded videos is only possible for key frames. In order to extract an arbitrary frame, you'll need to use a library like FFmpegMediaMetadataRetriever which uses native code to extract data from the video. It is very fast, comes with precompiled binaries (for ARM and x86) so you don't need to delve into C++ and makefiles, is licensed under Apache 2.0 and it comes with a demo Android app.

There is also a pure Java library, JCodec but it's slower and when I used it last year the colors of the extracted frame were distorted.

Crispert
  • 978
  • 6
  • 13
  • I tried JCodec, but I couldn't get anything but keyframes from a 240fps slow motion mp4. I ended up using opencv's Java bindings, the VideoCapture class. I believe it's using ffmpeg under the covers. I was able to get all the frames, and each fames's timestamp and the video's total number of frames as well. WARNING - this was the java sdk, I haven't tried the latest opencv for android yet. – medloh Oct 19 '15 at 15:35
3

you have to pass the path to this method...Perfectly working code ! hope it helpfull

gradle-- implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.15'

public void VideoToGif(String uri) {
    Uri videoFileUri = Uri.parse(uri);

    FFmpegMediaMetadataRetriever retriever = new FFmpegMediaMetadataRetriever();
    retriever.setDataSource(uri);
    List<Bitmap> rev = new ArrayList<Bitmap>();
    MediaPlayer mp = MediaPlayer.create(GitToImage.this, videoFileUri);
    int millis = mp.getDuration();
    System.out.println("starting point");
    for (int i = 100000; i <=millis * 1000; i += 100000*2) {
        Bitmap bitmap = retriever.getFrameAtTime(i, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
        rev.add(bitmap);
    }

    GiftoImage((ArrayList) rev);
}
Naveen
  • 31
  • 1
1

getFrameAt get data in milliseconds but you are incrementing .001 miliseconds in for loop.

    for(int i=1000000;i<millis*1000;i+=1000000) // for incrementing 1s use 1000
    {
       bArray.add(mRetriever.getFrameAtTime(i, 
                       MediaMetadataRetriever.OPTION_CLOSEST_SYNC));
    }

change it like above . Above is sample for creating what you want. I also answered it here

Community
  • 1
  • 1
Zar E Ahmer
  • 32,807
  • 18
  • 222
  • 281
0

Starting with Android 9.0 (API level 28), MediaMetadataRetriever has a getFrameAtIndex (int frameIndex) method, which accepts the zero-based index of the frame you want and returns a Bitmap.

See https://developer.android.com/reference/android/media/MediaMetadataRetriever.html#getFrameAtIndex(int)

Eran
  • 56
  • 2