diff --git a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java index 8a8e0fc94a9..4bb0e7899c7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java +++ b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java @@ -109,6 +109,7 @@ private void generateDataFrom(final Frameset frameset, final UUID updateRequestI final Stopwatch sw = Log.isLoggable(TAG, Log.DEBUG) ? Stopwatch.createStarted() : null; int currentPosMs = 0; + int pos = 1; final int urlFrameCount = frameset.getFramesPerPageX() * frameset.getFramesPerPageY(); @@ -117,14 +118,6 @@ private void generateDataFrom(final Frameset frameset, final UUID updateRequestI // get the bitmap final Bitmap srcBitMap = getBitMapFrom(url); - // It can happen, that the original bitmap could not be downloaded - // In such a case - we don't want a NullPointer - simply return null - // Recycled bitmaps can also not be created, so we also need to check for that - if (srcBitMap == null || srcBitMap.isRecycled()) { - Log.e(TAG, "Failed to retrieve or use bitmap from url: " + url); - continue; - } - // The data is not added directly to "seekbarPreviewData" due to // concurrency and checks for "updateRequestIdentifier" final var generatedDataForUrl = new SparseArrayCompat>(urlFrameCount); @@ -133,19 +126,34 @@ private void generateDataFrom(final Frameset frameset, final UUID updateRequestI // foreach frame in the returned bitmap for (int i = 0; i < urlFrameCount; i++) { // Frames outside the video length are skipped - if (i >= frameset.getTotalCount()) { + if (pos > frameset.getTotalCount()) { break; } // Get the bounds where the frame is found final int[] bounds = frameset.getFrameBoundsAt(currentPosMs); generatedDataForUrl.put(currentPosMs, () -> { + // It can happen, that the original bitmap could not be downloaded + // In such a case - we don't want a NullPointer - simply return null; + // Recycled bitmaps are also unusable here so we should check for that too + if (srcBitMap == null || srcBitMap.isRecycled()) { + return null; + } + // Cut out the corresponding bitmap form the "srcBitMap" - return Bitmap.createBitmap(srcBitMap, bounds[1], bounds[2], + final Bitmap cutOutBitmap = Bitmap.createBitmap(srcBitMap, bounds[1], bounds[2], frameset.getFrameWidth(), frameset.getFrameHeight()); + + // We need to copy the bitmap to create a new instance since createBitmap + // allows itself to return the original object that is was created with + // this leads to recycled bitmaps being returned (if they are identical) + // Reference: https://stackoverflow.com/a/23683075 + first comment + // Fixes: https://github.com/TeamNewPipe/NewPipe/issues/11461 + return cutOutBitmap.copy(cutOutBitmap.getConfig(), true); }); currentPosMs += frameset.getDurationPerFrame(); + pos++; } // Check if we are still the latest request