MediaMetadataRetriever .getFrameAtTime(long,option)崩溃

时间:2013-01-22 13:52:41

标签: android eclipse

我有一个循环,我试图每1/10秒从视频中提取一个帧。但是在19帧(视频的1.9秒)后,我在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

这是我正在使用的代码:

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

就像我说的,它应该得到~300帧,但只能设法在崩溃前提取19帧,但我不明白为什么会发生NULL指针错误。

提前致谢

6 个答案:

答案 0 :(得分:2)

您是否尝试过使用FFmpegMediaMetadataRetriever lib而不是MediaMetadataRetriever?

另外,从logcat上的这一行:

  

01-22 11:59:15.598:E / MediaMetadataRetrieverJNI(572):getFrameAtTime:   videoFrame是一个NULL指针

我会尝试记录for循环的每一行,例如Log.d(“MyApp”,“getFrameAtTime的参数”+(100000 * i));

我知道你已经知道哪个帧是问题(19/20),但只是看看在变成空指针之前该变量会发生什么(也许你可以使用Long i而不是int i?)。

另外,您是否尝试使用上述建议来使用MediaMetadataRetriever.OPTION_CLOSEST_SYNC而不是OPTION_CLOSEST?

答案 1 :(得分:1)

我在运行Android 4.0的Evo V上得到类似的行为:我测试视频的前六帧转换得很好,然后我开始获取空指针,即使我构建一个新的MediaMetadataRetriever再试一次。

我的日志还显示:&#34; MediaMetadataRetriever服务器已经死亡!&#34;

有一种方法可以避免这个问题,并且取决于您尝试做什么,它可能是合适的:传递MediaMetadataRetriever.OPTION_CLOSEST_SYNC而不是OPTION_CLOSEST。

在我的测试中,当我使用OPTION_CLOSEST_SYNC时,崩溃总是消失。

这对我的应用程序没什么帮助,因为实际上我确实需要非常接近的帧,而不仅仅是指定的&#34;同步帧。&#34;但它可能更适合你。

答案 2 :(得分:0)

我发现你的错误是分裂而不是相乘...... 将milli转换为micro需要乘以1000,而你正在进行除法......

使用MediaPlayer获取视频的持续时间..

 file = new File(Environment.getExternalStorageDirectory(), "myvideo.mp4");
 MediaPlayer mp = MediaPlayer.create(MainActivity.this,Uri.parse(file.getAbsolutePath()));
            int duration = mp.getDuration();// it gives duration in milliseconds 
            mp.release();

将其与duration*1000

相乘

循环直到

     for(int i=0 ;i <5; i++)
                {
                  if(looper < duration *1000)
                  {
                      ImageView imageView = (ImageView)findViewById(ids_of_images[i]);


imageView.setImageBitmap(retriever.getFrameAtTime(looper,MediaMetadataRetriever.OPTION_CLOSEST));

                        looper +=1000000;  
                  }

                }

答案 3 :(得分:0)

这在android bug数据库中报告为MediaMetadataRetriever.getFrameAtTime() failing/crashing。此问题已过时关闭。我在运行Android 4.2.2的Tesco HUDL 1.0(HT7S3)上遇到过它。

添加以下重试允许我的代码工作。

        if( bmp == null){
            mMediaData = new MediaMetadataRetriever();
            mMediaData.setDataSource( /* same parameters as before */ );
            bmp = mMediaData.getFrameAtTime( /* same parameters */ );
        }

只有早期版本的Android我在我的测试中看到了这个,并且从错误的“过时”结束,它是在更新版本中修复的东西。

因此,在后续重新查询之前重新创建媒体检索器的解决方法可能是最好的。

在我的设备上重新构建mediaRetriever需要大约3秒钟,获取位图需要.1s,所以我每次都无法负担得起它。

答案 4 :(得分:0)

报告了一个OPTION_CLOSEST错误,请参阅https://code.google.com/p/android/issues/detail?id=193194

Google 2016年8月31日的回复

您好, 开发团队已修复您已报告的问题,并将在未来的版本中提供 谢谢 状态:FutureRelease

答案 5 :(得分:0)

public Bitmap getVideoFrame() {
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            Uri data = Uri.parse(videoPath);=

            AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.testvid);
            if (afd != null) {
                retriever.setDataSource(afd.getFileDescriptor());
            }
            myVideoView.setVideoPath(videoPath);
            myVideoView.start();
            Bitmap tmp = retriever.getFrameAtTime();
                Log.e(TAG,"Retrievertmp " + tmp);

            return tmp;
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        } catch (RuntimeException ex) {
            ex.printStackTrace();
        } finally {
            try {
                retriever.release();
            } catch (RuntimeException ex) {
            }
        }
        return null;
    }

tmp始终为空。

日志:MediaMetadataRetrieverJNI:getFrameAtTime:videoFrame是NULL指针

任何人都可以帮忙吗?