From bd91af604b4bf24075e1d5f868a5ebf3d209eeb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Paul=20K=C3=BChne?= <felix@feepk.net> Date: Sun, 26 Jan 2020 14:58:24 +0100 Subject: [PATCH] macosx/library: add an image cache to improve responsiveness --- .../macosx/VLC.xcodeproj/project.pbxproj | 6 + modules/gui/macosx/Makefile.am | 2 + .../library/VLCLibraryAlbumTableCellView.m | 7 +- .../library/VLCLibraryAudioDataSource.m | 7 +- .../library/VLCLibraryCollectionViewItem.m | 9 +- .../gui/macosx/library/VLCLibraryController.h | 1 - .../gui/macosx/library/VLCLibraryController.m | 17 --- .../gui/macosx/library/VLCLibraryDataTypes.h | 1 + .../gui/macosx/library/VLCLibraryDataTypes.m | 6 + .../gui/macosx/library/VLCLibraryImageCache.h | 36 +++++ .../gui/macosx/library/VLCLibraryImageCache.m | 123 ++++++++++++++++++ .../library/VLCLibraryInformationPanel.m | 4 +- po/POTFILES.in | 2 + 13 files changed, 180 insertions(+), 41 deletions(-) create mode 100644 modules/gui/macosx/library/VLCLibraryImageCache.h create mode 100644 modules/gui/macosx/library/VLCLibraryImageCache.m diff --git a/extras/package/macosx/VLC.xcodeproj/project.pbxproj b/extras/package/macosx/VLC.xcodeproj/project.pbxproj index 4534baf5cc25..38aa696d996e 100644 --- a/extras/package/macosx/VLC.xcodeproj/project.pbxproj +++ b/extras/package/macosx/VLC.xcodeproj/project.pbxproj @@ -139,6 +139,7 @@ 7D903EAF224392B400917358 /* timespec_get.c in Sources */ = {isa = PBXBuildFile; fileRef = 7D903EAE224392B400917358 /* timespec_get.c */; }; 7D903EB6224394BE00917358 /* specific.c in Sources */ = {isa = PBXBuildFile; fileRef = 7D903EB5224394BE00917358 /* specific.c */; }; 7D903EB92243952100917358 /* threads.c in Sources */ = {isa = PBXBuildFile; fileRef = 7D903EB82243952100917358 /* threads.c */; }; + 7D92AF2123DDCA8D00D81EA3 /* VLCLibraryImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D92AF2023DDCA8D00D81EA3 /* VLCLibraryImageCache.m */; }; 7D93D8FC2316C2DC001C0063 /* VLCCustomCropArWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D93D8FB2316C2DC001C0063 /* VLCCustomCropArWindowController.m */; }; 7DB40D2A20CBCEB500F63173 /* VLCMainMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DB40D2920CBCEB500F63173 /* VLCMainMenu.m */; }; 7DB40D2D20CBCEC200F63173 /* VLCStatusBarIcon.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DB40D2B20CBCEC200F63173 /* VLCStatusBarIcon.m */; }; @@ -540,6 +541,8 @@ 7D903EAE224392B400917358 /* timespec_get.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = timespec_get.c; path = ../../../../compat/timespec_get.c; sourceTree = "<group>"; }; 7D903EB5224394BE00917358 /* specific.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = specific.c; path = ../../../src/darwin/specific.c; sourceTree = "<group>"; }; 7D903EB82243952100917358 /* threads.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = threads.c; path = ../../../../src/misc/threads.c; sourceTree = "<group>"; }; + 7D92AF1F23DDCA8D00D81EA3 /* VLCLibraryImageCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryImageCache.h; sourceTree = "<group>"; }; + 7D92AF2023DDCA8D00D81EA3 /* VLCLibraryImageCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryImageCache.m; sourceTree = "<group>"; }; 7D93D8F92316C142001C0063 /* VLCCustomCropARPanel.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VLCCustomCropARPanel.xib; sourceTree = "<group>"; }; 7D93D8FA2316C2DC001C0063 /* VLCCustomCropArWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCCustomCropArWindowController.h; sourceTree = "<group>"; }; 7D93D8FB2316C2DC001C0063 /* VLCCustomCropArWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCCustomCropArWindowController.m; sourceTree = "<group>"; }; @@ -1052,6 +1055,8 @@ 7DFBDCAA2269E77F00B700A5 /* VLCLibraryModel.m */, 7DFBDCB2226CD00900B700A5 /* VLCLibraryDataTypes.h */, 7DFBDCB3226CD00900B700A5 /* VLCLibraryDataTypes.m */, + 7D92AF1F23DDCA8D00D81EA3 /* VLCLibraryImageCache.h */, + 7D92AF2023DDCA8D00D81EA3 /* VLCLibraryImageCache.m */, 7DFBDCAC2269ED0C00B700A5 /* VLCLibraryVideoDataSource.h */, 7DFBDCAD2269ED0C00B700A5 /* VLCLibraryVideoDataSource.m */, 7DE2F0422282C84A0040DD0A /* VLCLibraryAudioDataSource.h */, @@ -1754,6 +1759,7 @@ 7DE9C7DD220728420089108F /* VLCPlayerController.m in Sources */, 7D93D8FC2316C2DC001C0063 /* VLCCustomCropArWindowController.m in Sources */, 1C3113941E508C6900D4DD76 /* VLCAddonsWindowController.m in Sources */, + 7D92AF2123DDCA8D00D81EA3 /* VLCLibraryImageCache.m in Sources */, 7D2E0EDE20CD206F0033A221 /* VLCVideoWindowCommon.m in Sources */, 1C3113961E508C6900D4DD76 /* applescript.m in Sources */, 1C3113981E508C6900D4DD76 /* VLCAudioEffectsWindowController.m in Sources */, diff --git a/modules/gui/macosx/Makefile.am b/modules/gui/macosx/Makefile.am index 53d36083f923..f273ccb7cf28 100644 --- a/modules/gui/macosx/Makefile.am +++ b/modules/gui/macosx/Makefile.am @@ -64,6 +64,8 @@ libmacosx_plugin_la_SOURCES = \ gui/macosx/library/VLCLibraryController.m \ gui/macosx/library/VLCLibraryDataTypes.h \ gui/macosx/library/VLCLibraryDataTypes.m \ + gui/macosx/library/VLCLibraryImageCache.h \ + gui/macosx/library/VLCLibraryImageCache.m \ gui/macosx/library/VLCLibraryInformationPanel.h \ gui/macosx/library/VLCLibraryInformationPanel.m \ gui/macosx/library/VLCLibraryMenuController.h \ diff --git a/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m b/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m index 4fc79b3ae87f..c02442c77642 100644 --- a/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m +++ b/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m @@ -213,12 +213,7 @@ const CGFloat LayoutSpacer; VLCMediaLibraryMediaItem *mediaItem = _tracks[row]; - NSImage *image; - if (mediaItem.smallArtworkGenerated) { - if (mediaItem.smallArtworkMRL.length > 0) { - image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:mediaItem.smallArtworkMRL]]; - } - } + NSImage *image = mediaItem.smallArtworkImage; if (!image) { image = [NSImage imageNamed: @"noart.png"]; } diff --git a/modules/gui/macosx/library/VLCLibraryAudioDataSource.m b/modules/gui/macosx/library/VLCLibraryAudioDataSource.m index 6512f276855f..d711d96d52bc 100644 --- a/modules/gui/macosx/library/VLCLibraryAudioDataSource.m +++ b/modules/gui/macosx/library/VLCLibraryAudioDataSource.m @@ -189,12 +189,7 @@ static NSString *VLCAudioLibraryCellIdentifier = @"VLCAudioLibraryCellIdentifier { VLCMediaLibraryMediaItem *mediaItem = _displayedCollection[row]; - NSImage *image; - if (mediaItem.smallArtworkGenerated) { - if (mediaItem.smallArtworkMRL.length > 0) { - image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:mediaItem.smallArtworkMRL]]; - } - } + NSImage *image = mediaItem.smallArtworkImage; if (!image) { image = [NSImage imageNamed: @"noart.png"]; } diff --git a/modules/gui/macosx/library/VLCLibraryCollectionViewItem.m b/modules/gui/macosx/library/VLCLibraryCollectionViewItem.m index ac97cfe2111d..f6487a17d9b8 100644 --- a/modules/gui/macosx/library/VLCLibraryCollectionViewItem.m +++ b/modules/gui/macosx/library/VLCLibraryCollectionViewItem.m @@ -185,14 +185,7 @@ const CGFloat VLCLibraryCollectionViewItemMaximumDisplayedProgress = 0.95; - (NSImage *)imageForMedia { - NSImage *image; - if (_representedMediaItem.smallArtworkGenerated) { - image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:_representedMediaItem.smallArtworkMRL]]; - } else { - if (_representedMediaItem.mediaType != VLC_ML_MEDIA_TYPE_AUDIO) { - [_libraryController attemptToGenerateThumbnailForMediaItem:_representedMediaItem]; - } - } + NSImage *image = _representedMediaItem.smallArtworkImage; if (!image) { image = [NSImage imageNamed: @"noart.png"]; } diff --git a/modules/gui/macosx/library/VLCLibraryController.h b/modules/gui/macosx/library/VLCLibraryController.h index 95a19981f138..cb18fe436f67 100644 --- a/modules/gui/macosx/library/VLCLibraryController.h +++ b/modules/gui/macosx/library/VLCLibraryController.h @@ -36,7 +36,6 @@ NS_ASSUME_NONNULL_BEGIN - (int)appendItemToPlaylist:(VLCMediaLibraryMediaItem *)mediaItem playImmediately:(BOOL)playImmediately; - (int)appendItemsToPlaylist:(NSArray <VLCMediaLibraryMediaItem *> *)mediaItemArray playFirstItemImmediately:(BOOL)playFirstItemImmediately; - (void)showItemInFinder:(VLCMediaLibraryMediaItem *)mediaItem; -- (int)attemptToGenerateThumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem; - (int)addFolderWithFileURL:(NSURL *)fileURL; - (int)banFolderWithFileURL:(NSURL *)fileURL; diff --git a/modules/gui/macosx/library/VLCLibraryController.m b/modules/gui/macosx/library/VLCLibraryController.m index a0e955019bc6..b20b48ade82d 100644 --- a/modules/gui/macosx/library/VLCLibraryController.m +++ b/modules/gui/macosx/library/VLCLibraryController.m @@ -30,10 +30,6 @@ #import <vlc_media_library.h> -uint32_t kVLCDesiredThumbnailWidth = 512; -uint32_t kVLCDesiredThumbnailHeight = 320; -float kVLCDefaultThumbnailPosition = .15; - @interface VLCLibraryController() { vlc_medialibrary_t *_p_libraryInstance; @@ -140,19 +136,6 @@ float kVLCDefaultThumbnailPosition = .15; } } -- (int)attemptToGenerateThumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem -{ - if (!_p_libraryInstance) { - return VLC_ENOOBJ; - } - return vlc_ml_media_generate_thumbnail(_p_libraryInstance, - mediaItem.libraryID, - VLC_ML_THUMBNAIL_SMALL, - kVLCDesiredThumbnailWidth, - kVLCDesiredThumbnailHeight, - kVLCDefaultThumbnailPosition); -} - #pragma mark - folder management - (int)addFolderWithFileURL:(NSURL *)fileURL diff --git a/modules/gui/macosx/library/VLCLibraryDataTypes.h b/modules/gui/macosx/library/VLCLibraryDataTypes.h index 01f67a81c12c..bada8467f8a5 100644 --- a/modules/gui/macosx/library/VLCLibraryDataTypes.h +++ b/modules/gui/macosx/library/VLCLibraryDataTypes.h @@ -175,6 +175,7 @@ extern const long long int VLCMediaLibraryMediaItemDurationDenominator; @property (readonly) NSString *title; @property (readonly, nullable) NSString *smallArtworkMRL; +@property (readonly, nullable) NSImage *smallArtworkImage; @property (readonly) BOOL smallArtworkGenerated; @property (readonly) BOOL favorited; diff --git a/modules/gui/macosx/library/VLCLibraryDataTypes.m b/modules/gui/macosx/library/VLCLibraryDataTypes.m index d29094da13c1..57555e087967 100644 --- a/modules/gui/macosx/library/VLCLibraryDataTypes.m +++ b/modules/gui/macosx/library/VLCLibraryDataTypes.m @@ -25,6 +25,7 @@ #import "main/VLCMain.h" #import "extensions/NSString+Helpers.h" #import "library/VLCInputItem.h" +#import "library/VLCLibraryImageCache.h" #import <vlc_url.h> @@ -461,6 +462,11 @@ NSString *VLCMediaLibraryMediaItemLibraryID = @"VLCMediaLibraryMediaItemLibraryI } } +- (NSImage *)smallArtworkImage +{ + return [VLCLibraryImageCache thumbnailForMediaItem:self]; +} + - (VLCInputItem *)inputItem { input_item_t *p_inputItem = vlc_ml_get_input_item(_p_mediaLibrary, _libraryID); diff --git a/modules/gui/macosx/library/VLCLibraryImageCache.h b/modules/gui/macosx/library/VLCLibraryImageCache.h new file mode 100644 index 000000000000..18e6cef04d85 --- /dev/null +++ b/modules/gui/macosx/library/VLCLibraryImageCache.h @@ -0,0 +1,36 @@ +/***************************************************************************** +* VLCLibraryImageCache.h: MacOS X interface module +***************************************************************************** +* Copyright (C) 2020 VLC authors and VideoLAN +* +* Authors: Felix Paul Kühne <fkuehne # videolan -dot- org> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +*****************************************************************************/ + +#import <Cocoa/Cocoa.h> + +NS_ASSUME_NONNULL_BEGIN + +@class VLCMediaLibraryMediaItem; + +@interface VLCLibraryImageCache : NSObject + ++ (NSImage *)thumbnailForMediaItemWithID:(int64_t)libraryID; ++ (NSImage *)thumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/gui/macosx/library/VLCLibraryImageCache.m b/modules/gui/macosx/library/VLCLibraryImageCache.m new file mode 100644 index 000000000000..29255f2873cd --- /dev/null +++ b/modules/gui/macosx/library/VLCLibraryImageCache.m @@ -0,0 +1,123 @@ +/***************************************************************************** +* VLCLibraryImageCache.m: MacOS X interface module +***************************************************************************** +* Copyright (C) 2020 VLC authors and VideoLAN +* +* Authors: Felix Paul Kühne <fkuehne # videolan -dot- org> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +*****************************************************************************/ + + +#import "VLCLibraryImageCache.h" +#import "library/VLCLibraryDataTypes.h" +#import "main/VLCMain.h" + +NSUInteger kVLCMaximumImageCacheSize = 50; +uint32_t kVLCDesiredThumbnailWidth = 512; +uint32_t kVLCDesiredThumbnailHeight = 320; +float kVLCDefaultThumbnailPosition = .15; + + +@interface VLCLibraryImageCache() +{ + NSCache *_imageCache; + vlc_medialibrary_t *_p_libraryInstance; +} + +@end + +@implementation VLCLibraryImageCache + +- (instancetype)init +{ + self = [super init]; + if (self) { + _imageCache = [[NSCache alloc] init]; + _imageCache.countLimit = kVLCMaximumImageCacheSize; + } + return self; +} + ++ (instancetype)sharedImageCache +{ + static dispatch_once_t onceToken; + static VLCLibraryImageCache *sharedImageCache; + dispatch_once(&onceToken, ^{ + sharedImageCache = [[VLCLibraryImageCache alloc] init]; + }); + return sharedImageCache; +} + ++ (NSImage *)thumbnailForMediaItemWithID:(int64_t)libraryID +{ + return [[VLCLibraryImageCache sharedImageCache] imageForMediaItemWithID:libraryID]; +} + ++ (NSImage *)thumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem +{ + return [[VLCLibraryImageCache sharedImageCache] imageForMediaItem:mediaItem]; +} + +- (NSImage *)imageForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem +{ + NSImage *cachedImage = [_imageCache objectForKey:@(mediaItem.libraryID)]; + if (cachedImage) { + return cachedImage; + } + return [self smallThumbnailForMediaItem:mediaItem]; +} + +- (NSImage *)imageForMediaItemWithID:(int64_t)libraryID +{ + NSNumber *libraryIDnumber = @(libraryID); + NSImage *cachedImage = [_imageCache objectForKey:libraryIDnumber]; + if (cachedImage) { + return cachedImage; + } + VLCMediaLibraryMediaItem *mediaItem = [VLCMediaLibraryMediaItem mediaItemForLibraryID:libraryID]; + return [self smallThumbnailForMediaItem:mediaItem]; +} + +- (NSImage *)smallThumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem +{ + NSImage *image; + if (mediaItem.smallArtworkGenerated) { + image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:mediaItem.smallArtworkMRL]]; + } else { + if (mediaItem.mediaType != VLC_ML_MEDIA_TYPE_AUDIO) { + [self generateThumbnailForMediaItem:mediaItem.libraryID]; + } + } + if (image) { + [_imageCache setObject:image forKey:@(mediaItem.libraryID)]; + } + return image; +} + +- (void)generateThumbnailForMediaItem:(int64_t)mediaID +{ + if (!_p_libraryInstance) { + _p_libraryInstance = vlc_ml_instance_get(getIntf()); + } + vlc_ml_media_generate_thumbnail(_p_libraryInstance, + mediaID, + VLC_ML_THUMBNAIL_SMALL, + kVLCDesiredThumbnailWidth, + kVLCDesiredThumbnailHeight, + kVLCDefaultThumbnailPosition); +} + +@end diff --git a/modules/gui/macosx/library/VLCLibraryInformationPanel.m b/modules/gui/macosx/library/VLCLibraryInformationPanel.m index 793105f6d330..54b0e0a3e62f 100644 --- a/modules/gui/macosx/library/VLCLibraryInformationPanel.m +++ b/modules/gui/macosx/library/VLCLibraryInformationPanel.m @@ -92,9 +92,7 @@ self.multiLineTextLabel.stringValue = textContent; self.window.title = _representedMediaItem.title; - if (_representedMediaItem.smallArtworkMRL) { - self.imageView.image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:_representedMediaItem.smallArtworkMRL]]; - } + self.imageView.image = _representedMediaItem.smallArtworkImage; } @end diff --git a/po/POTFILES.in b/po/POTFILES.in index 2ca218a6210d..21287b2f21a1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -471,6 +471,8 @@ modules/gui/macosx/library/VLCLibraryController.h modules/gui/macosx/library/VLCLibraryController.m modules/gui/macosx/library/VLCLibraryDataTypes.h modules/gui/macosx/library/VLCLibraryDataTypes.m +modules/gui/macosx/library/VLCLibraryImageCache.h +modules/gui/macosx/library/VLCLibraryImageCache.m modules/gui/macosx/library/VLCLibraryInformationPanel.h modules/gui/macosx/library/VLCLibraryInformationPanel.m modules/gui/macosx/library/VLCLibraryMenuController.h -- GitLab