Java 为MediaMetadataRetriever[Android]线程时出现NullPointerException

Java 为MediaMetadataRetriever[Android]线程时出现NullPointerException,java,android,multithreading,nullpointerexception,mediametadataretriever,Java,Android,Multithreading,Nullpointerexception,Mediametadataretriever,我再次对一个错误失去了想法,并返回有用的Stack Overflow社区寻求指导。我知道这是一个很长的任务,但我相信它可以帮助社区中的其他人,并为退伍军人提供挑战 本质上,我正在构建一个逐帧处理视频的应用程序。最初,此过程是线性完成的,但速度是此应用程序的主要关注点。自然的想法是可以实现单独的线程来处理帧。每个线程只是前一个循环的一个迭代;从视频中检索帧,对其进行处理,并将结果数据放入数组的正确索引中 问题在于,在从循环到线程的转换过程中,当尝试访问MediaMetadataRetriever.

我再次对一个错误失去了想法,并返回有用的Stack Overflow社区寻求指导。我知道这是一个很长的任务,但我相信它可以帮助社区中的其他人,并为退伍军人提供挑战

本质上,我正在构建一个逐帧处理视频的应用程序。最初,此过程是线性完成的,但速度是此应用程序的主要关注点。自然的想法是可以实现单独的线程来处理帧。每个线程只是前一个循环的一个迭代;从视频中检索帧,对其进行处理,并将结果数据放入数组的正确索引中

问题在于,在从循环到线程的转换过程中,当尝试访问MediaMetadataRetriever.getFrameAtTime()返回的帧时,有时会抛出NullPointerException(大约每200帧一次)

下面是一些可能有用的代码部分:

摘自连接到启动处理的按钮的onClickListener。onClickListener启动ThreadPoolExecutor,该执行器管理用于帧处理的线程

long videoLength = getVideoLength(videoUri);
coords = new coordinate[(int)(videoLength/frameIntervalInMicroSeconds)];
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    Runtime.getRuntime().availableProcessors()+1,
    Runtime.getRuntime().availableProcessors()+1,
    1,
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<Runnable>());
for (long a = 0; a < videoLength; a += frameIntervalInMicroSeconds) {
    new ProcessFrame().executeOnExecutor(executor, (long) a);
摘自ProcessFrame,ThreadPoolExecutor运行的线程(作为异步任务)。如上所述,线程只需获取帧、处理帧并将其放置在数组中的正确位置

protected Void doInBackground(Long... frameTime){
    /* Excluded variable declarations */

    Bitmap bMap;
    synchronized (this) {
        bMap = getVideoFrame(videoUri, frameTime[0]);
    }
    try {
        //This line below is where the NullPointerException is thrown
        bMap = Bitmap.createScaledBitmap(bMap, bMap.getWidth() / RESIZE_FACTOR, bMap.getHeight() / RESIZE_FACTOR, true);
    }catch (Exception e){
        System.out.println("Caught NullPointerException");
        return null;
    }

    /* I excluded the frame processing done here */

    //add coordinate to the list
    try {
        synchronized (this){
            coords[(int) (frameTime[0] / frameIntervalInMicroSeconds)] = temp;
        }
    }catch (Exception e){
        System.out.println("Caught out of bounds coordinate");
    }

    /* excluded clean up */
}
访问空帧时产生的错误消息:

7043-7856/com.example.tyler.laserphysicsappb E/MediaMetadataRetrieverJNI﹕ getFrameAtTime: videoFrame is a NULL pointer
一般评论、观察和我尝试过的事情:

引发异常的行在最后一个代码块中用注释标记

如果没有try/catch功能块,我的手机在此异常情况下不会显示正常的应用程序崩溃消息,它会闪烁一个黑屏,然后快速返回主屏幕。(我通过在黑屏闪烁时快速拍摄logcat的屏幕截图,发现了它是哪一行)

如果没有try/catch块,我尝试的另一部手机会忽略错误并继续,但这会破坏结果

在bMap=getVideoFrame(videoUri,frameTime[0])周围添加同步块似乎降低了错误的发生率,但这种情况仍然存在

我试过ffmpegMediaMetadataRetriever替代品。这个包并没有消除错误,并且减慢了处理时间

一旦抛出nullPointerException,所有后续线程似乎更有可能再次抛出该异常

使用最多一个线程运行ThreadPoolExecutor不会产生错误


即使我将doInBackground()的整个主体包含在一个同步块中,错误仍然会出现

我已经能够修复该错误,但结果并不明显。我发现阻止异常出现的唯一方法是在MediaMetadataRetriever getFrameAtTime()函数调用周围放置一个同步块。下面是新的getVideoFrame方法

private Bitmap getVideoFrame(Uri uri, long timeInUSeconds) {
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    retriever.setDataSource(this, uri);
    synchronize (this) {
        Bitmap temp = retriever.getFrameAtTime(timeInUSeconds, MediaMetadataRetriever.OPTION_CLOSEST);
    }
    retriever.release();
    return temp;
}

遗憾的是,由于这修复了错误,速度问题并没有消除,因为获取帧仍然需要大量的时间,远远超过实际处理帧的时间。然而,pr

尝试同步这一行:
bMap=Bitmap.createScaledBimat(bMap,bMap.getWidth()/RESIZE\u FACTOR,bMap.getHeight()/RESIZE\u FACTOR,true)<代码>位图。createScaledBitmap()
不是您的方法,因此您可能不知道它在幕后做什么…它是一个静态方法,因此如果两个线程同时调用它,可能会出现问题。我感谢您的快速回复,但这并不能解决问题。不过,我将把这些变化留在这里,因为它们似乎不会使情况变得更糟,而且从长远来看可能会有所帮助。谢谢。没问题。很抱歉它没有解决您的问题。。。
private Bitmap getVideoFrame(Uri uri, long timeInUSeconds) {
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    retriever.setDataSource(this, uri);
    synchronize (this) {
        Bitmap temp = retriever.getFrameAtTime(timeInUSeconds, MediaMetadataRetriever.OPTION_CLOSEST);
    }
    retriever.release();
    return temp;
}