У меня еще раз закончились идеи об ошибке и я вернулся в полезное сообщество переполнения стека. Я понимаю, что это очень длинный пост, чтобы пробраться через него, но я считаю, что он может быть полезен другим в сообществе и предоставляет вызов для ветеранов.NullPointerException при потоковой передаче MediaMetadataRetriever [Android]
По существу, я создаю приложение, которое обрабатывает видео кадр за кадром. Первоначально этот процесс выполнялся линейно, но скорость - это серьезная проблема для этого приложения. Естественная мысль заключалась в том, что для обработки кадров могут быть реализованы отдельные потоки. Каждый поток будет просто одной итерацией предыдущего цикла; извлечение кадра из видео, его обработку и размещение полученных данных в правильный индекс массива.
Вопрос заключается в том, что при переходе от цикла к резьбе, A NullPointerException иногда выбрасывается (примерно один раз каждые 200 кадров), при попытке доступа кадра, возвращенный MediaMetadataRetriever.getFrameAtTime()
Вот некоторые разделы кода, которые могут быть полезны:
Выписка из 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 для видео, и время в микросекундах, и возвращает кадр видео как Bitmap
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, поток (как ASyncTask), который выполняется 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 мой телефон не отображает сообщение об ошибке обычного приложения в этом исключении, оно мигает черным экраном и затем быстро возвращается на главный экран. (Я обнаружил, какая строка была, быстро сняв скриншот логарифма, пока черный экран мигал)
Без блока try/catch другой телефон, который я попробовал, просто игнорирует ошибку и продолжает, но это разрушает результаты.
Добавление синхронизированного блока вокруг bMap = getVideoFrame (videoUri, frameTime [0]), по-видимому, сделало ошибку менее распространенной, но она все еще происходит.
Я пробовал вариант ffmpegMediaMetadataRetriever. Этот пакет не устранил ошибку и замедлил время обработки.
После исключения nullPointerException все последующие потоки, похоже, с большей вероятностью снова выдадут исключение.
Запуск ThreadPoolExecutor с максимум одним потоком не вызывает ошибки.
Даже если я прилагаю все тело doInBackground() в Синхронизировать блоке, ошибка все равно появляется
Попробуйте «синхронизировать» эту строку: 'bMap = Bitmap.createScaledBitmap (bMap, bMap.getWidth()/RESIZE_FACTOR, bMap.getHeight()/RESIZE_FACTOR, true);'. 'Bitmap.createScaledBitmap()' не ваш метод, поэтому вы, вероятно, не знаете, что он делает за кулисами ... Это статический метод, поэтому могут возникнуть проблемы, если 2 потока одновременно вызовут его. – brso05
Я ценю ваш быстрый ответ, но это не решает проблему. Я оставлю все изменения, хотя они, похоже, не ухудшают ситуацию, и это может быть полезно в долгосрочной перспективе. Спасибо. – TyN101
без проблем жаль, что это не исправить вашу проблему ... – brso05