[vlc-commits] [Git][videolan/vlc][master] 5 commits: macosx: Avoid blocking main and global queues when acquiring thumbnails

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sat Jul 4 18:01:57 UTC 2026



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
bb49ee7a by Claudio Cambra at 2026-07-04T19:38:03+02:00
macosx: Avoid blocking main and global queues when acquiring thumbnails

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
ecc44894 by Claudio Cambra at 2026-07-04T19:38:03+02:00
macosx: Limit thumbnail image generation iteration to expected composite image count

Avoid unneeded work

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
9fffe2ab by Claudio Cambra at 2026-07-04T19:38:03+02:00
macosx: Deduplicate iterateMediaItemsWithBlock implementations

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
bd3ff021 by Claudio Cambra at 2026-07-04T19:38:03+02:00
macosx: Add enumeration of media items method with stop parameter

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
43d24a49 by Claudio Cambra at 2026-07-04T19:38:03+02:00
macosx: Avoid duplicates an no-art images in composite images

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -


4 changed files:

- modules/gui/macosx/library/VLCLibraryDataTypes.h
- modules/gui/macosx/library/VLCLibraryDataTypes.m
- modules/gui/macosx/library/VLCLibraryImageCache.m
- modules/gui/macosx/library/audio-library/VLCLibraryAllAudioGroupsMediaLibraryItem.m


Changes:

=====================================
modules/gui/macosx/library/VLCLibraryDataTypes.h
=====================================
@@ -144,6 +144,7 @@ typedef NS_ENUM(NSUInteger, VLCMediaLibraryParentGroupType) {
 - (int)setFavorite:(BOOL)favorite;
 - (int)toggleFavorite;
 
+- (void)enumerateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem *item, BOOL *stop))mediaItemBlock;
 - (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock;
 
 @end


=====================================
modules/gui/macosx/library/VLCLibraryDataTypes.m
=====================================
@@ -366,9 +366,22 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
     [self.firstMediaItem revealInFinder];
 }
 
+- (void)enumerateMediaItemsWithBlock:(nonnull void (^)(VLCMediaLibraryMediaItem * _Nonnull, BOOL * _Nonnull))mediaItemBlock
+{
+    for (VLCMediaLibraryMediaItem * const item in self.mediaItems) {
+        BOOL stop = NO;
+        mediaItemBlock(item, &stop);
+        if (stop) {
+            break;
+        }
+    }
+}
+
 - (void)iterateMediaItemsWithBlock:(nonnull void (^)(VLCMediaLibraryMediaItem * _Nonnull))mediaItemBlock
 {
-    [self doesNotRecognizeSelector:_cmd];
+    [self enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+        mediaItemBlock(item);
+    }];
 }
 
 - (NSArray<NSString *> *)labels
@@ -582,8 +595,28 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
 
 - (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock
 {
-    for(VLCMediaLibraryAlbum* album in self.albums) {
-        [album iterateMediaItemsWithBlock:mediaItemBlock];
+    [self enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+        mediaItemBlock(item);
+    }];
+}
+
+- (void)enumerateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem *item, BOOL *stop))mediaItemBlock
+{
+    __block BOOL shouldStop = NO;
+    for (VLCMediaLibraryAlbum * const album in self.albums) {
+        if (shouldStop) {
+            break;
+        }
+        [album enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+            if (shouldStop) {
+                *stop = YES;
+                return;
+            }
+            mediaItemBlock(item, stop);
+            if (*stop) {
+                shouldStop = YES;
+            }
+        }];
     }
 }
 
@@ -708,13 +741,6 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
     return self.genres.firstObject;
 }
 
-- (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock
-{
-    for(VLCMediaLibraryMediaItem* mediaItem in self.mediaItems) {
-        mediaItemBlock(mediaItem);
-    }
-}
-
 - (int)setFavorite:(BOOL)favorite
 {
     const int res = setFavoriteForLibraryItem(vlc_ml_album_set_favorite, self.libraryID, favorite);
@@ -805,10 +831,25 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
 - (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock
 {
     // By default iterate album-by-album
-    [self iterateMediaItemsWithBlock:mediaItemBlock orderedBy:VLC_ML_PARENT_ALBUM];
+    [self enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+        mediaItemBlock(item);
+    } orderedBy:VLC_ML_PARENT_ALBUM];
+}
+
+- (void)enumerateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem *item, BOOL *stop))mediaItemBlock
+{
+    [self enumerateMediaItemsWithBlock:mediaItemBlock orderedBy:VLC_ML_PARENT_ALBUM];
 }
 
 - (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock orderedBy:(int)mediaItemParentType
+{
+    [self enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+        mediaItemBlock(item);
+    } orderedBy:mediaItemParentType];
+}
+
+- (void)enumerateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem *item, BOOL *stop))mediaItemBlock
+                       orderedBy:(int)mediaItemParentType
 {
     NSArray<id<VLCMediaLibraryItemProtocol>> *childItems;
     switch(mediaItemParentType) {
@@ -824,8 +865,21 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
             break;
     }
 
-    for(id<VLCMediaLibraryItemProtocol> childItem in childItems) {
-        [childItem iterateMediaItemsWithBlock:mediaItemBlock];
+    __block BOOL shouldStop = NO;
+    for (id<VLCMediaLibraryItemProtocol> const childItem in childItems) {
+        if (shouldStop) {
+            break;
+        }
+        [childItem enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+            if (shouldStop) {
+                *stop = YES;
+                return;
+            }
+            mediaItemBlock(item, stop);
+            if (*stop) {
+                shouldStop = YES;
+            }
+        }];
     }
 }
 
@@ -922,13 +976,6 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
     return self.groupMediaItems;
 }
 
-- (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock
-{
-    for (VLCMediaLibraryMediaItem * const item in self.mediaItems) {
-        [item iterateMediaItemsWithBlock:mediaItemBlock];
-    }
-}
-
 @end
 
 @interface VLCMediaLibraryPlaylist ()
@@ -1131,17 +1178,6 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
     }
 }
 
-- (void)iterateMediaItemsWithBlock:(nonnull void (^)(VLCMediaLibraryMediaItem * _Nonnull))mediaItemBlock
-{
-    if (self.mediaItems == nil) {
-        [self fetchMediaItems];
-    }
-
-    for(VLCMediaLibraryMediaItem * const item in self.mediaItems) {
-        mediaItemBlock(item);
-    }
-}
-
 - (int)setFavorite:(BOOL)favorite
 {
     const int res = setFavoriteForLibraryItem(vlc_ml_playlist_set_favorite, self.libraryID, favorite);
@@ -1552,7 +1588,15 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
 
 - (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock
 {
-    mediaItemBlock(self);
+    [self enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+        mediaItemBlock(item);
+    }];
+}
+
+- (void)enumerateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem *item, BOOL *stop))mediaItemBlock
+{
+    BOOL stop = NO;
+    mediaItemBlock(self, &stop);
 }
 
 #pragma mark - preference setters / getters
@@ -1872,13 +1916,6 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
     return self.episodes;
 }
 
-- (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock
-{
-    for (VLCMediaLibraryMediaItem * const item in self.mediaItems) {
-        mediaItemBlock(item);
-    }
-}
-
 @end
 
 @implementation VLCMediaLibraryMovie
@@ -1914,13 +1951,6 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
     return self.mediaItems.firstObject;
 }
 
-- (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock
-{
-    for (VLCMediaLibraryMediaItem *item in self.mediaItems) {
-        mediaItemBlock(item);
-    }
-}
-
 @end
 
 @implementation VLCMediaLibraryEntryPoint
@@ -2017,8 +2047,19 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
 
 - (void)iterateMediaItemsWithBlock:(nonnull void (^)(VLCMediaLibraryMediaItem * _Nonnull))mediaItemBlock
 {
-     for (VLCMediaLibraryMediaItem * const childItem in self.mediaItems) {
-        mediaItemBlock(childItem);
+    [self enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+        mediaItemBlock(item);
+    }];
+}
+
+- (void)enumerateMediaItemsWithBlock:(nonnull void (^)(VLCMediaLibraryMediaItem * _Nonnull, BOOL * _Nonnull))mediaItemBlock
+{
+    for (VLCMediaLibraryMediaItem * const childItem in self.mediaItems) {
+        BOOL stop = NO;
+        mediaItemBlock(childItem, &stop);
+        if (stop) {
+            break;
+        }
     }
 }
 


=====================================
modules/gui/macosx/library/VLCLibraryImageCache.m
=====================================
@@ -275,43 +275,61 @@ const NSUInteger kVLCCompositeImageDefaultCompositedGridItemCount = 4;
         ![libraryItem isKindOfClass:VLCMediaLibraryMediaItem.class]) {
 
         dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
-            NSMutableArray<NSImage *> * const itemImages = [NSMutableArray array];
+            NSMutableSet<NSString *> * const seenMRLs = NSMutableSet.set;
+            NSMutableArray<VLCMediaLibraryMediaItem *> * const uniqueItems =
+                [NSMutableArray arrayWithCapacity:kVLCCompositeImageDefaultCompositedGridItemCount];
+
+            [libraryItem enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+                if (uniqueItems.count == (NSUInteger)kVLCCompositeImageDefaultCompositedGridItemCount) {
+                    *stop = YES;
+                    return;
+                }
+                NSString * const mrl = item.smallArtworkMRL;
+                if (mrl == nil || [seenMRLs containsObject:mrl]) {
+                    return;
+                }
+                [seenMRLs addObject:mrl];
+                [uniqueItems addObject:item];
+            }];
+
+            if (uniqueItems.count == 0) {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    completionHandler(cache->_noArtImage);
+                });
+                return;
+            }
+
+            NSMutableArray<NSImage *> * const itemImages =
+                [NSMutableArray arrayWithCapacity:uniqueItems.count];
             dispatch_group_t const group = dispatch_group_create();
 
-            [libraryItem iterateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item) {
+            for (VLCMediaLibraryMediaItem * const item in uniqueItems) {
                 dispatch_group_enter(group);
                 [cache imageForLibraryItem:item withCompletion:^(const NSImage * thumbnail) {
                     NSImage * const mutableRef = (NSImage *)thumbnail;
-                    @synchronized (itemImages) {
-                        if (mutableRef && ![mutableRef isEqual:cache->_noArtImage]) {
+                    if (mutableRef && ![mutableRef isEqual:cache->_noArtImage]) {
+                        @synchronized (itemImages) {
                             [itemImages addObject:mutableRef];
                         }
                     }
                     dispatch_group_leave(group);
                 }];
-            }];
-
-            dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
-
-            NSArray<NSImage *> *uniqueImages;
-            @synchronized (itemImages) {
-                uniqueImages = [NSOrderedSet orderedSetWithArray:itemImages].array;
             }
 
-            if (uniqueImages.count == 0) {
-                dispatch_async(dispatch_get_main_queue(), ^{
+            dispatch_group_notify(group, dispatch_get_main_queue(), ^{
+                if (itemImages.count == 0) {
                     completionHandler(cache->_noArtImage);
-                });
-                return;
-            }
+                    return;
+                }
 
-            const NSSize size = NSMakeSize(kVLCDesiredThumbnailWidth, kVLCDesiredThumbnailHeight);
-            NSArray<NSValue *> * const frames =
-                [NSImage framesForCompositeImageSquareGridWithImages:uniqueImages size:size gridItemCount:kVLCCompositeImageDefaultCompositedGridItemCount];
-            NSImage * const compositeImage =
-                [NSImage compositeImageWithImages:uniqueImages frames:frames size:size];
+                const NSSize size = NSMakeSize(kVLCDesiredThumbnailWidth, kVLCDesiredThumbnailHeight);
+                NSArray<NSValue *> * const frames =
+                    [NSImage framesForCompositeImageSquareGridWithImages:itemImages
+                                                                    size:size
+                                                           gridItemCount:kVLCCompositeImageDefaultCompositedGridItemCount];
+                NSImage * const compositeImage =
+                    [NSImage compositeImageWithImages:itemImages frames:frames size:size];
 
-            dispatch_async(dispatch_get_main_queue(), ^{
                 completionHandler(compositeImage);
             });
         });


=====================================
modules/gui/macosx/library/audio-library/VLCLibraryAllAudioGroupsMediaLibraryItem.m
=====================================
@@ -66,10 +66,29 @@
 
 - (void)iterateMediaItemsWithBlock:(nonnull void (^)(VLCMediaLibraryMediaItem * _Nonnull))mediaItemBlock
 {
-    // Iterate by album
+    [self enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+        mediaItemBlock(item);
+    }];
+}
+
+- (void)enumerateMediaItemsWithBlock:(nonnull void (^)(VLCMediaLibraryMediaItem * _Nonnull, BOOL * _Nonnull))mediaItemBlock
+{
     NSArray<id<VLCMediaLibraryItemProtocol>> * const childItems = self.albums;
-    for(id<VLCMediaLibraryItemProtocol> childItem in childItems) {
-        [childItem iterateMediaItemsWithBlock:mediaItemBlock];
+    __block BOOL shouldStop = NO;
+    for (id<VLCMediaLibraryItemProtocol> const childItem in childItems) {
+        if (shouldStop) {
+            break;
+        }
+        [childItem enumerateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item, BOOL * const stop) {
+            if (shouldStop) {
+                *stop = YES;
+                return;
+            }
+            mediaItemBlock(item, stop);
+            if (*stop) {
+                shouldStop = YES;
+            }
+        }];
     }
 }
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/5519a21dc1cc968963a2c74b03b2718c0a3216c5...43d24a4921a0d4ae9d2402eb9f7d939d96a555ef

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/5519a21dc1cc968963a2c74b03b2718c0a3216c5...43d24a4921a0d4ae9d2402eb9f7d939d96a555ef
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list