Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MediaMetadataRetriever .getFrameAtTime(long, option) crashing

I've got a loop where I'm trying to extract a frame from a video every 1/10th of a second. But after 19 frames (1.9s of the video), I get the following error on Logcat:

01-22 11:59:15.498: E/OMXCodec(38): [OMX.google.h264.decoder] Timed out waiting for output buffers: 0/0
01-22 11:59:15.598: E/MetadataRetrieverClient(38): failed to capture a video frame
01-22 11:59:15.598: E/MediaMetadataRetrieverJNI(572): getFrameAtTime: videoFrame is a NULL pointer
01-22 11:59:15.598: D/AndroidRuntime(572): Shutting down VM
01-22 11:59:15.598: W/dalvikvm(572): threadid=1: thread exiting with uncaught exception (group=0x409961f8)
01-22 11:59:15.608: E/AndroidRuntime(572): FATAL EXCEPTION: main
01-22 11:59:15.608: E/AndroidRuntime(572): java.lang.NullPointerException

This is the code I'm using:

File videoPath = new File(Environment.getExternalStorageDirectory(), "test.mp4");
String video = videoPath.getAbsolutePath();
MediaMetadataRetriever vidFile = new MediaMetadataRetriever();
vidFile.setDataSource(video);

//Create folder to store images
String storageFolder = "/Storage";
String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
File newFolder = new File(extStorageDirectory + storageFolder);
newFolder.mkdir();

String value = vidFile.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
long vidLength = (Long.parseLong(value)/1000); //Returns milliseconds - divide by 1,000
//Video length = 30037ms - result is 30.037s

for(int i = 0; i <= 10*vidLength; i++, image++) //10*vidLength since I'm getting frames every 1/10th sec
{
    Bitmap bmp = vidFile.getFrameAtTime(100000*i, MediaMetadataRetriever.OPTION_CLOSEST);
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    bmp.compress(Bitmap.CompressFormat.PNG, 100, bytes);
    String imagename = String.format(Locale.ENGLISH, "%03d", image);
    File f = new File(Environment.getExternalStorageDirectory() + storageFolder + File.separator + imagename + ".png");
    f.createNewFile();
    FileOutputStream fo = new FileOutputStream(f);
    fo.write(bytes.toByteArray());
    fo.close();

    //Don't seem to make a difference one way or the other
    bytes.flush(); 
    bytes.close();  
}

Like I said, it should get ~300 frames, but only manages to extract 19 frames before crashing, but I don't see why the NULL pointer error is happening.

Thanks in advance

like image 758
zodac Avatar asked Oct 22 '22 19:10

zodac


2 Answers

Have you tried using FFmpegMediaMetadataRetriever lib instead of MediaMetadataRetriever?

Also, from this line on your logcat:

01-22 11:59:15.598: E/MediaMetadataRetrieverJNI(572): getFrameAtTime: videoFrame is a NULL pointer

I would try making it log every line of the for loop, like Log.d("MyApp","Param of getFrameAtTime"+(100000*i));

I know you already know which frame is the problem (19/20), but just to see what happens with that variable before becoming a null pointer (maybe you could work with Long i instead of int i?).

Also, did you try the suggestion above to use MediaMetadataRetriever.OPTION_CLOSEST_SYNC instead of OPTION_CLOSEST?

like image 87
Luciano Pinheiro Avatar answered Oct 24 '22 10:10

Luciano Pinheiro


I get similar behavior on my Evo V running Android 4.0: the first six frames of my test video convert just fine, then I start getting null pointers back, even if I construct a new MediaMetadataRetriever to try again.

My logs also show: "MediaMetadataRetriever server died!"

There is a way to avoid this problem and depending on what you are trying to do it may be suitable: pass MediaMetadataRetriever.OPTION_CLOSEST_SYNC instead of OPTION_CLOSEST.

In my tests, the crashes always go away when I use OPTION_CLOSEST_SYNC.

This is not much help for my application, because I actually do need frames at very close intervals, not just the designated "sync frames." But it may be more acceptable in yours.

like image 30
Tom Boutell Avatar answered Oct 24 '22 10:10

Tom Boutell