11package org .schabi .newpipe .player .seekbarpreview ;
22
33import static org .schabi .newpipe .player .seekbarpreview .SeekbarPreviewThumbnailHelper .SeekbarPreviewThumbnailType ;
4+ import static org .schabi .newpipe .player .seekbarpreview .SeekbarPreviewThumbnailHelper .getSeekbarPreviewThumbnailType ;
45
56import android .content .Context ;
67import android .graphics .Bitmap ;
78import android .util .Log ;
89
910import androidx .annotation .NonNull ;
1011import androidx .annotation .Nullable ;
12+ import androidx .collection .SparseArrayCompat ;
1113
1214import com .google .common .base .Stopwatch ;
1315
1416import org .schabi .newpipe .extractor .stream .Frameset ;
1517import org .schabi .newpipe .util .PicassoHelper ;
1618
1719import java .util .Comparator ;
18- import java .util .HashMap ;
1920import java .util .List ;
20- import java .util .Map ;
2121import java .util .Optional ;
2222import java .util .UUID ;
23- import java .util .concurrent .ConcurrentHashMap ;
2423import java .util .concurrent .ExecutorService ;
2524import java .util .concurrent .Executors ;
2625import java .util .function .Supplier ;
@@ -34,18 +33,15 @@ public class SeekbarPreviewThumbnailHolder {
3433
3534 // Key = Position of the picture in milliseconds
3635 // Supplier = Supplies the bitmap for that position
37- private final Map <Integer , Supplier <Bitmap >> seekbarPreviewData = new ConcurrentHashMap <>();
36+ private final SparseArrayCompat <Supplier <Bitmap >> seekbarPreviewData =
37+ new SparseArrayCompat <>();
3838
3939 // This ensures that if the reset is still undergoing
4040 // and another reset starts, only the last reset is processed
4141 private UUID currentUpdateRequestIdentifier = UUID .randomUUID ();
4242
43- public synchronized void resetFrom (
44- @ NonNull final Context context ,
45- final List <Frameset > framesets ) {
46-
47- final int seekbarPreviewType =
48- SeekbarPreviewThumbnailHelper .getSeekbarPreviewThumbnailType (context );
43+ public void resetFrom (@ NonNull final Context context , final List <Frameset > framesets ) {
44+ final int seekbarPreviewType = getSeekbarPreviewThumbnailType (context );
4945
5046 final UUID updateRequestIdentifier = UUID .randomUUID ();
5147 this .currentUpdateRequestIdentifier = updateRequestIdentifier ;
@@ -63,13 +59,12 @@ public synchronized void resetFrom(
6359 executorService .shutdown ();
6460 }
6561
66- private void resetFromAsync (
67- final int seekbarPreviewType ,
68- final List <Frameset > framesets ,
69- final UUID updateRequestIdentifier ) {
70-
62+ private void resetFromAsync (final int seekbarPreviewType , final List <Frameset > framesets ,
63+ final UUID updateRequestIdentifier ) {
7164 Log .d (TAG , "Clearing seekbarPreviewData" );
72- seekbarPreviewData .clear ();
65+ synchronized (seekbarPreviewData ) {
66+ seekbarPreviewData .clear ();
67+ }
7368
7469 if (seekbarPreviewType == SeekbarPreviewThumbnailType .NONE ) {
7570 Log .d (TAG , "Not processing seekbarPreviewData due to settings" );
@@ -94,10 +89,8 @@ private void resetFromAsync(
9489 generateDataFrom (frameset , updateRequestIdentifier );
9590 }
9691
97- private Frameset getFrameSetForType (
98- final List <Frameset > framesets ,
99- final int seekbarPreviewType ) {
100-
92+ private Frameset getFrameSetForType (final List <Frameset > framesets ,
93+ final int seekbarPreviewType ) {
10194 if (seekbarPreviewType == SeekbarPreviewThumbnailType .HIGH_QUALITY ) {
10295 Log .d (TAG , "Strategy for seekbarPreviewData: high quality" );
10396 return framesets .stream ()
@@ -111,17 +104,14 @@ private Frameset getFrameSetForType(
111104 }
112105 }
113106
114- private void generateDataFrom (
115- final Frameset frameset ,
116- final UUID updateRequestIdentifier ) {
117-
107+ private void generateDataFrom (final Frameset frameset , final UUID updateRequestIdentifier ) {
118108 Log .d (TAG , "Starting generation of seekbarPreviewData" );
119109 final Stopwatch sw = Log .isLoggable (TAG , Log .DEBUG ) ? Stopwatch .createStarted () : null ;
120110
121111 int currentPosMs = 0 ;
122112 int pos = 1 ;
123113
124- final int frameCountPerUrl = frameset .getFramesPerPageX () * frameset .getFramesPerPageY ();
114+ final int urlFrameCount = frameset .getFramesPerPageX () * frameset .getFramesPerPageY ();
125115
126116 // Process each url in the frameset
127117 for (final String url : frameset .getUrls ()) {
@@ -130,11 +120,11 @@ private void generateDataFrom(
130120
131121 // The data is not added directly to "seekbarPreviewData" due to
132122 // concurrency and checks for "updateRequestIdentifier"
133- final Map < Integer , Supplier < Bitmap >> generatedDataForUrl = new HashMap <>( );
123+ final var generatedDataForUrl = new SparseArrayCompat < Supplier < Bitmap >>( urlFrameCount );
134124
135125 // The bitmap consists of several images, which we process here
136126 // foreach frame in the returned bitmap
137- for (int i = 0 ; i < frameCountPerUrl ; i ++) {
127+ for (int i = 0 ; i < urlFrameCount ; i ++) {
138128 // Frames outside the video length are skipped
139129 if (pos > frameset .getTotalCount ()) {
140130 break ;
@@ -161,15 +151,17 @@ private void generateDataFrom(
161151 // Check if we are still the latest request
162152 // If not abort method execution
163153 if (isRequestIdentifierCurrent (updateRequestIdentifier )) {
164- seekbarPreviewData .putAll (generatedDataForUrl );
154+ synchronized (seekbarPreviewData ) {
155+ seekbarPreviewData .putAll (generatedDataForUrl );
156+ }
165157 } else {
166158 Log .d (TAG , "Aborted of generation of seekbarPreviewData" );
167159 break ;
168160 }
169161 }
170162
171163 if (sw != null ) {
172- Log .d (TAG , "Generation of seekbarPreviewData took " + sw .stop (). toString () );
164+ Log .d (TAG , "Generation of seekbarPreviewData took " + sw .stop ());
173165 }
174166 }
175167
@@ -189,17 +181,14 @@ private Bitmap getBitMapFrom(final String url) {
189181 final Bitmap bitmap = PicassoHelper .loadSeekbarThumbnailPreview (url ).get ();
190182
191183 if (sw != null ) {
192- Log .d (TAG ,
193- "Download of bitmap for seekbarPreview from '" + url
194- + "' took " + sw .stop ().toString ());
184+ Log .d (TAG , "Download of bitmap for seekbarPreview from '" + url + "' took "
185+ + sw .stop ());
195186 }
196187
197188 return bitmap ;
198189 } catch (final Exception ex ) {
199- Log .w (TAG ,
200- "Failed to get bitmap for seekbarPreview from url='" + url
201- + "' in time" ,
202- ex );
190+ Log .w (TAG , "Failed to get bitmap for seekbarPreview from url='" + url
191+ + "' in time" , ex );
203192 return null ;
204193 }
205194 }
@@ -208,32 +197,20 @@ private boolean isRequestIdentifierCurrent(final UUID requestIdentifier) {
208197 return this .currentUpdateRequestIdentifier .equals (requestIdentifier );
209198 }
210199
211-
212200 public Optional <Bitmap > getBitmapAt (final int positionInMs ) {
213- // Check if the BitmapData is empty
214- if (seekbarPreviewData .isEmpty ()) {
215- return Optional .empty ();
216- }
217-
218- // Get the closest frame to the requested position
219- final int closestIndexPosition =
220- seekbarPreviewData .keySet ().stream ()
221- .min (Comparator .comparingInt (i -> Math .abs (i - positionInMs )))
222- .orElse (-1 );
223-
224- // this should never happen, because
225- // it indicates that "seekbarPreviewData" is empty which was already checked
226- if (closestIndexPosition == -1 ) {
227- return Optional .empty ();
201+ // Get the frame supplier closest to the requested position
202+ Supplier <Bitmap > closestFrame = () -> null ;
203+ synchronized (seekbarPreviewData ) {
204+ int min = Integer .MAX_VALUE ;
205+ for (int i = 0 ; i < seekbarPreviewData .size (); i ++) {
206+ final int pos = Math .abs (seekbarPreviewData .keyAt (i ) - positionInMs );
207+ if (pos < min ) {
208+ closestFrame = seekbarPreviewData .valueAt (i );
209+ min = pos ;
210+ }
211+ }
228212 }
229213
230- try {
231- // Get the bitmap for the position (executes the supplier)
232- return Optional .ofNullable (seekbarPreviewData .get (closestIndexPosition ).get ());
233- } catch (final Exception ex ) {
234- // If there is an error, log it and return Optional.empty
235- Log .w (TAG , "Unable to get seekbar preview" , ex );
236- return Optional .empty ();
237- }
214+ return Optional .ofNullable (closestFrame .get ());
238215 }
239216}
0 commit comments