@@ -132,17 +132,8 @@ private void generateDataFrom(final Frameset frameset, final UUID updateRequestI
132132
133133 // Get the bounds where the frame is found
134134 final int [] bounds = frameset .getFrameBoundsAt (currentPosMs );
135- generatedDataForUrl .put (currentPosMs , () -> {
136- // It can happen, that the original bitmap could not be downloaded
137- // In such a case - we don't want a NullPointer - simply return null
138- if (srcBitMap == null ) {
139- return null ;
140- }
141-
142- // Cut out the corresponding bitmap form the "srcBitMap"
143- return Bitmap .createBitmap (srcBitMap , bounds [1 ], bounds [2 ],
144- frameset .getFrameWidth (), frameset .getFrameHeight ());
145- });
135+ generatedDataForUrl .put (currentPosMs ,
136+ createBitmapSupplier (srcBitMap , bounds , frameset ));
146137
147138 currentPosMs += frameset .getDurationPerFrame ();
148139 pos ++;
@@ -165,6 +156,45 @@ private void generateDataFrom(final Frameset frameset, final UUID updateRequestI
165156 }
166157 }
167158
159+ private Supplier <Bitmap > createBitmapSupplier (final Bitmap srcBitMap ,
160+ final int [] bounds ,
161+ final Frameset frameset ) {
162+ return () -> {
163+ // It can happen, that the original bitmap could not be downloaded
164+ // (or it was recycled though that should not happen)
165+ // In such a case - we don't want a NullPointer/
166+ // "cannot use a recycled source in createBitmap" Exception -> simply return null
167+ if (srcBitMap == null || srcBitMap .isRecycled ()) {
168+ return null ;
169+ }
170+
171+ // Under some rare circumstances the youtube api returns slightly too small storyboards,
172+ // (or not the matching frame width/height)
173+ // This would lead to createBitmap cutting out a bitmap that is out of bounds,
174+ // so we need to adjust the bounds accordingly
175+ if (srcBitMap .getWidth () < bounds [1 ] + frameset .getFrameWidth ()) {
176+ bounds [1 ] = srcBitMap .getWidth () - frameset .getFrameWidth ();
177+ }
178+
179+ if (srcBitMap .getHeight () < bounds [2 ] + frameset .getFrameHeight ()) {
180+ bounds [2 ] = srcBitMap .getHeight () - frameset .getFrameHeight ();
181+ }
182+
183+ // Cut out the corresponding bitmap form the "srcBitMap"
184+ final Bitmap cutOutBitmap = Bitmap .createBitmap (srcBitMap , bounds [1 ], bounds [2 ],
185+ frameset .getFrameWidth (), frameset .getFrameHeight ());
186+
187+ // If the cut out bitmap is identical to its source,
188+ // we need to copy the bitmap to create a new instance.
189+ // createBitmap allows itself to return the original object that is was created with
190+ // this leads to recycled bitmaps being returned (if they are identical)
191+ // Reference: https://stackoverflow.com/a/23683075 + first comment
192+ // Fixes: https://github.com/TeamNewPipe/NewPipe/issues/11461
193+ return cutOutBitmap == srcBitMap
194+ ? cutOutBitmap .copy (cutOutBitmap .getConfig (), true ) : cutOutBitmap ;
195+ };
196+ }
197+
168198 @ Nullable
169199 private Bitmap getBitMapFrom (final String url ) {
170200 if (url == null ) {
0 commit comments