线程化 MediaMetadataRetriever [Android] 时出现 NullPointerException
NullPointerException when threading MediaMetadataRetriever [Android]
我再次 运行 出错误的想法,并返回到有用的 Stack Overflow 社区寻求指导。我知道这是一个很长的 post 过程,但我相信它会对社区中的其他人有所帮助,并为退伍军人提供挑战。
本质上,我正在构建一个逐帧处理视频的应用程序。最初,这个过程是线性完成的,但速度是这个应用程序的主要关注点。自然的想法是可以实现单独的线程来处理帧。每个线程只是前一个循环的一次迭代;从视频中检索帧,对其进行处理,并将结果数据放入数组的正确索引中。
问题是,在从循环过渡到线程的过程中,当尝试访问 MediaMetadataRetriever.getFrameAtTime()[=15= 返回的帧时,有时会抛出 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 和以微秒为单位的时间的辅助方法,returns 视频帧作为位图
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
一般评论、观察和我尝试过的事情:
抛出异常的行在最后一个代码块中用注释标记。
没有try/catch块,我的phone不会在这个异常上显示正常的app crashed消息,闪黑屏,然后快速returns到家屏幕。 (黑屏一闪,赶紧截图logcat才发现是哪一行)
没有 try/catch 块,我尝试的另一个 phone 只是忽略错误并继续,但这会破坏结果。
在 bMap = getVideoFrame(videoUri, frameTime[0]) 周围添加同步块似乎使错误不太常见,但它仍然会发生。
我尝试了 ffmpegMediaMetadataRetriever 替代方案。这个包没有消除错误,并且拖慢了处理时间。
一旦抛出 nullPointerException,所有后续线程似乎更有可能再次抛出异常。
运行 ThreadPoolExecutor最多1个线程不报错
即使我将 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
我再次 运行 出错误的想法,并返回到有用的 Stack Overflow 社区寻求指导。我知道这是一个很长的 post 过程,但我相信它会对社区中的其他人有所帮助,并为退伍军人提供挑战。
本质上,我正在构建一个逐帧处理视频的应用程序。最初,这个过程是线性完成的,但速度是这个应用程序的主要关注点。自然的想法是可以实现单独的线程来处理帧。每个线程只是前一个循环的一次迭代;从视频中检索帧,对其进行处理,并将结果数据放入数组的正确索引中。
问题是,在从循环过渡到线程的过程中,当尝试访问 MediaMetadataRetriever.getFrameAtTime()[=15= 返回的帧时,有时会抛出 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 和以微秒为单位的时间的辅助方法,returns 视频帧作为位图
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
一般评论、观察和我尝试过的事情:
抛出异常的行在最后一个代码块中用注释标记。
没有try/catch块,我的phone不会在这个异常上显示正常的app crashed消息,闪黑屏,然后快速returns到家屏幕。 (黑屏一闪,赶紧截图logcat才发现是哪一行)
没有 try/catch 块,我尝试的另一个 phone 只是忽略错误并继续,但这会破坏结果。
在 bMap = getVideoFrame(videoUri, frameTime[0]) 周围添加同步块似乎使错误不太常见,但它仍然会发生。
我尝试了 ffmpegMediaMetadataRetriever 替代方案。这个包没有消除错误,并且拖慢了处理时间。
一旦抛出 nullPointerException,所有后续线程似乎更有可能再次抛出异常。
运行 ThreadPoolExecutor最多1个线程不报错
即使我将 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