【问题标题】:NullPointerException when threading MediaMetadataRetriever [Android]线程 MediaMetadataRetriever [Android] 时出现 NullPointerException
【发布时间】:2015-06-28 00:08:42
【问题描述】:

我再次对错误没有想法,并返回有用的 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);

getVideoFrame(),一个辅助方法,它接收一个视频的 Uri 和一个以微秒为单位的时间,并将视频的帧作为位图返回

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

摘自 ProcessFrame,由 ThreadPoolExecutor 运行的线程(作为 ASyncTask)。如上所述,线程只是简单地获取帧,处理帧,并将其放入数组中的正确位置。

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

一般 cmets、观察结果和我尝试过的事情:

抛出异常的行在最后一个代码块中用注释标记。

如果没有try/catch块,我的手机不会在这个异常上显示正常的应用程序崩溃消息,它会闪烁黑屏,然后快速返回主屏幕。 (黑屏一闪,快速截取logcat的截图才知道是哪一行)

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

在 bMap = getVideoFrame(videoUri, frameTime[0]) 周围添加同步块似乎使错误不那么常见,但它仍然会发生。

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

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

以最多一个线程运行 ThreadPoolExecutor 不会产生错误。

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

【问题讨论】:

  • 试试synchronizing 这一行:bMap = Bitmap.createScaledBitmap(bMap, bMap.getWidth() / RESIZE_FACTOR, bMap.getHeight() / RESIZE_FACTOR, true);Bitmap.createScaledBitmap() 不是你的方法,所以你可能不知道它在幕后做什么......它是一个静态方法,所以如果 2 个线程同时调用它可能会出现问题。
  • 感谢您的快速回复,但这并不能解决问题。不过,我会保留这些更改,因为它们似乎不会使情况变得更糟,从长远来看,它可能会有所帮助。谢谢。
  • 没问题,抱歉没有解决您的问题...

标签: java android multithreading nullpointerexception mediametadataretriever


【解决方案1】:

我已经能够修复错误,但效果不佳。我发现阻止异常出现的唯一方法是在 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;
}

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

【讨论】:

    猜你喜欢
    • 2013-11-09
    • 2016-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多