Skip to content

Commit

Permalink
Significantly improve memory usage of loading tags
Browse files Browse the repository at this point in the history
This especially helps with bad or brutal files.

Signed-off-by: Christopher Snowhill <[email protected]>
  • Loading branch information
kode54 committed Oct 3, 2023
1 parent d88a90e commit 8c67b6c
Show file tree
Hide file tree
Showing 11 changed files with 350 additions and 297 deletions.
34 changes: 19 additions & 15 deletions Playlist/PlaylistLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -746,25 +746,29 @@ - (void)loadInfoForEntries:(NSArray *)entries {
NSURL *url = urlForPath(key);

[op addExecutionBlock:^{
DLog(@"Loading metadata for %@", url);
[[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", url];
@autoreleasepool {
DLog(@"Loading metadata for %@", url);
[[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", url];

NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:url];
if(entryProperties == nil)
return;
NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:url];
if(entryProperties == nil)
return;

NSDictionary *entryMetadata = [AudioMetadataReader metadataForURL:url];
NSDictionary *entryMetadata = [AudioMetadataReader metadataForURL:url];

NSDictionary *entryInfo = [NSDictionary dictionaryByMerging:entryProperties with:entryMetadata];
NSDictionary *entryInfo = [NSDictionary dictionaryByMerging:entryProperties with:entryMetadata];

[weakLock lock];
entryInfo = [weakDataStore coalesceEntryInfo:entryInfo];
[weakArray addObject:key];
[weakArray addObject:entryInfo];
[uniquePathsEntries setObject:[[NSMutableArray alloc] init] forKey:key];
progress += progressstep;
[self setProgressJobStatus:progress];
[weakLock unlock];
[weakLock lock];
@autoreleasepool {
entryInfo = [weakDataStore coalesceEntryInfo:entryInfo];
}
[weakArray addObject:key];
[weakArray addObject:entryInfo];
[uniquePathsEntries setObject:[[NSMutableArray alloc] init] forKey:key];
progress += progressstep;
[self setProgressJobStatus:progress];
[weakLock unlock];
}
}];

[queue addOperation:op];
Expand Down
176 changes: 91 additions & 85 deletions Plugins/FFMPEG/FFMPEGDecoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -562,75 +562,77 @@ - (void)updateMetadata {
}
tag = NULL;
while((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
if(!strcasecmp(tag->key, "streamtitle")) {
NSString *artistTitle = guess_encoding_of_string(tag->value);
NSArray *splitValues = [artistTitle componentsSeparatedByString:@" - "];
NSString *_artist = @"";
NSString *_title = [splitValues objectAtIndex:0];
if([splitValues count] > 1) {
_artist = _title;
_title = [splitValues objectAtIndex:1];
setDictionary(_metaDict, @"artist", _artist);
setDictionary(_metaDict, @"title", _title);
} else {
setDictionary(_metaDict, @"title", _title);
}
} else if(!strcasecmp(tag->key, "unsynced lyrics") ||
!strcasecmp(tag->key, "lyrics")) {
setDictionary(_metaDict, @"unsyncedlyrics", guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "icy-url")) {
setDictionary(_metaDict, @"album", guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "icy-genre")) {
setDictionary(_metaDict, @"genre", guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "title")) {
NSString *_tag = guess_encoding_of_string(tag->value);
if(i == 0 && formatCtx->nb_chapters > 1) {
setDictionary(_metaDict, @"album", _tag);
} else {
setDictionary(_metaDict, @"title", _tag);
}
} else if(!strcasecmp(tag->key, "date_recorded")) {
setDictionary(_metaDict, @"date", guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "replaygain_gain")) {
// global or chapter gain
NSString *tagName;
if(i == 0)
tagName = @"replaygain_album_gain";
else
tagName = @"replaygain_track_gain";
setDictionary(_metaDict, tagName, guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "replaygain_peak")) {
// global or chapter peak
NSString *tagName;
if(i == 0)
tagName = @"replaygain_album_peak";
else
tagName = @"replaygain_track_peak";
setDictionary(_metaDict, tagName, guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "iTunNORM")) {
NSString *tagString = guess_encoding_of_string(tag->value);
NSArray *tag = [tagString componentsSeparatedByString:@" "];
NSMutableArray *wantedTag = [[NSMutableArray alloc] init];
for(size_t i = 0; i < [tag count]; ++i) {
NSString *tagValue = tag[i];
if([tagValue length] == 8) {
[wantedTag addObject:tagValue];
@autoreleasepool {
if(!strcasecmp(tag->key, "streamtitle")) {
NSString *artistTitle = guess_encoding_of_string(tag->value);
NSArray *splitValues = [artistTitle componentsSeparatedByString:@" - "];
NSString *_artist = @"";
NSString *_title = [splitValues objectAtIndex:0];
if([splitValues count] > 1) {
_artist = _title;
_title = [splitValues objectAtIndex:1];
setDictionary(_metaDict, @"artist", _artist);
setDictionary(_metaDict, @"title", _title);
} else {
setDictionary(_metaDict, @"title", _title);
}
} else if(!strcasecmp(tag->key, "unsynced lyrics") ||
!strcasecmp(tag->key, "lyrics")) {
setDictionary(_metaDict, @"unsyncedlyrics", guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "icy-url")) {
setDictionary(_metaDict, @"album", guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "icy-genre")) {
setDictionary(_metaDict, @"genre", guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "title")) {
NSString *_tag = guess_encoding_of_string(tag->value);
if(i == 0 && formatCtx->nb_chapters > 1) {
setDictionary(_metaDict, @"album", _tag);
} else {
setDictionary(_metaDict, @"title", _tag);
}
} else if(!strcasecmp(tag->key, "date_recorded")) {
setDictionary(_metaDict, @"date", guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "replaygain_gain")) {
// global or chapter gain
NSString *tagName;
if(i == 0)
tagName = @"replaygain_album_gain";
else
tagName = @"replaygain_track_gain";
setDictionary(_metaDict, tagName, guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "replaygain_peak")) {
// global or chapter peak
NSString *tagName;
if(i == 0)
tagName = @"replaygain_album_peak";
else
tagName = @"replaygain_track_peak";
setDictionary(_metaDict, tagName, guess_encoding_of_string(tag->value));
} else if(!strcasecmp(tag->key, "iTunNORM")) {
NSString *tagString = guess_encoding_of_string(tag->value);
NSArray *tag = [tagString componentsSeparatedByString:@" "];
NSMutableArray *wantedTag = [[NSMutableArray alloc] init];
for(size_t i = 0; i < [tag count]; ++i) {
NSString *tagValue = tag[i];
if([tagValue length] == 8) {
[wantedTag addObject:tagValue];
}
}
if([wantedTag count] >= 10) {
NSScanner *scanner1 = [NSScanner scannerWithString:wantedTag[0]];
NSScanner *scanner2 = [NSScanner scannerWithString:wantedTag[1]];
unsigned int hexvalue1 = 0, hexvalue2 = 0;
[scanner1 scanHexInt:&hexvalue1];
[scanner2 scanHexInt:&hexvalue2];
float volume1 = -log10((double)(hexvalue1) / 1000) * 10;
float volume2 = -log10((double)(hexvalue2) / 1000) * 10;
float volumeToUse = MIN(volume1, volume2);
NSNumber *_volumeScale = @(pow(10, volumeToUse / 20));
setDictionary(_metaDict, @"volume", [_volumeScale stringValue]);
}
} else {
setDictionary(_metaDict, guess_encoding_of_string(tag->key), guess_encoding_of_string(tag->value));
}
if([wantedTag count] >= 10) {
NSScanner *scanner1 = [NSScanner scannerWithString:wantedTag[0]];
NSScanner *scanner2 = [NSScanner scannerWithString:wantedTag[1]];
unsigned int hexvalue1 = 0, hexvalue2 = 0;
[scanner1 scanHexInt:&hexvalue1];
[scanner2 scanHexInt:&hexvalue2];
float volume1 = -log10((double)(hexvalue1) / 1000) * 10;
float volume2 = -log10((double)(hexvalue2) / 1000) * 10;
float volumeToUse = MIN(volume1, volume2);
NSNumber *_volumeScale = @(pow(10, volumeToUse / 20));
setDictionary(_metaDict, @"volume", [_volumeScale stringValue]);
}
} else {
setDictionary(_metaDict, guess_encoding_of_string(tag->key), guess_encoding_of_string(tag->value));
}
}
}
Expand All @@ -639,29 +641,33 @@ - (void)updateMetadata {
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
HTTPSource *httpSource = (HTTPSource *)source;
if([httpSource hasMetadata]) {
NSDictionary *metadata = [httpSource metadata];
NSString *_genre = [metadata valueForKey:@"genre"];
NSString *_album = [metadata valueForKey:@"album"];
NSString *_artist = [metadata valueForKey:@"artist"];
NSString *_title = [metadata valueForKey:@"title"];

if(_genre && [_genre length]) {
[_metaDict setObject:@[_genre] forKey:@"genre"];
}
if(_album && [_album length]) {
[_metaDict setObject:@[_album] forKey:@"album"];
}
if(_artist && [_artist length]) {
[_metaDict setObject:@[_artist] forKey:@"artist"];
}
if(_title && [_title length]) {
[_metaDict setObject:@[_title] forKey:@"title"];
@autoreleasepool {
NSDictionary *metadata = [httpSource metadata];
NSString *_genre = [metadata valueForKey:@"genre"];
NSString *_album = [metadata valueForKey:@"album"];
NSString *_artist = [metadata valueForKey:@"artist"];
NSString *_title = [metadata valueForKey:@"title"];

if(_genre && [_genre length]) {
[_metaDict setObject:@[_genre] forKey:@"genre"];
}
if(_album && [_album length]) {
[_metaDict setObject:@[_album] forKey:@"album"];
}
if(_artist && [_artist length]) {
[_metaDict setObject:@[_artist] forKey:@"artist"];
}
if(_title && [_title length]) {
[_metaDict setObject:@[_title] forKey:@"title"];
}
}
}
}

if(![_metaDict isEqualToDictionary:metaDict]) {
metaDict = _metaDict;
@autoreleasepool {
metaDict = _metaDict;
}
if(![source seekable]) {
[self willChangeValueForKey:@"metadata"];
[self didChangeValueForKey:@"metadata"];
Expand Down
2 changes: 2 additions & 0 deletions Plugins/Flac/Flac.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
83747C4A2862DCF40021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
8384912D180816C900E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
83AA660A27B7DAE40098D4B8 /* cuesheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cuesheet.m; sourceTree = "<group>"; };
83C70A392ACC32FB004B60E4 /* RedundantPlaylistDataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RedundantPlaylistDataStore.h; path = ../../Utils/RedundantPlaylistDataStore.h; sourceTree = "<group>"; };
8D5B49B6048680CD000E48DA /* Flac.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Flac.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
Expand Down Expand Up @@ -93,6 +94,7 @@
08FB77AFFE84173DC02AAC07 /* Classes */ = {
isa = PBXGroup;
children = (
83C70A392ACC32FB004B60E4 /* RedundantPlaylistDataStore.h */,
834A42B4287AF7AA00EB9D9B /* AudioChunk.h */,
8301C145287805F500651A6E /* NSDictionary+Merge.h */,
8301C146287805F500651A6E /* NSDictionary+Merge.m */,
Expand Down
4 changes: 4 additions & 0 deletions Plugins/Flac/FlacDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#import "Plugin.h"

#import "RedundantPlaylistDataStore.h"

@interface FlacDecoder : NSObject <CogDecoder> {
FLAC__StreamDecoder *decoder;
void *blockBuffer;
Expand Down Expand Up @@ -42,6 +44,8 @@

BOOL cuesheetFound;
NSString *cuesheet;

RedundantPlaylistDataStore *dataStore;
}

- (void)setSource:(id<CogSource>)s;
Expand Down
61 changes: 35 additions & 26 deletions Plugins/Flac/FlacDecoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,16 @@ void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMeta
if(metadata->type == FLAC__METADATA_TYPE_CUESHEET && !flacDecoder->cuesheetFound) {
flacDecoder->cuesheetFound = YES;

NSString *_cuesheet;
grabbag__cuesheet_emit(&_cuesheet, metadata, [[NSString stringWithFormat:@"\"%@\"", [[[flacDecoder->source url] path] lastPathComponent]] UTF8String]);

if(![_cuesheet isEqual:flacDecoder->cuesheet]) {
flacDecoder->cuesheet = _cuesheet;
if(![flacDecoder->source seekable]) {
[flacDecoder willChangeValueForKey:@"metadata"];
[flacDecoder didChangeValueForKey:@"metadata"];
@autoreleasepool {
NSString *_cuesheet;
grabbag__cuesheet_emit(&_cuesheet, metadata, [[NSString stringWithFormat:@"\"%@\"", [[[flacDecoder->source url] path] lastPathComponent]] UTF8String]);

if(![_cuesheet isEqual:flacDecoder->cuesheet]) {
flacDecoder->cuesheet = _cuesheet;
if(![flacDecoder->source seekable]) {
[flacDecoder willChangeValueForKey:@"metadata"];
[flacDecoder didChangeValueForKey:@"metadata"];
}
}
}
}
Expand All @@ -241,25 +243,29 @@ void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMeta
char *_name;
char *_value;
if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(vorbis_comment->comments[i], &_name, &_value)) {
NSString *name = guess_encoding_of_string(_name);
NSString *value = guess_encoding_of_string(_value);
free(_name);
free(_value);
name = [name lowercaseString];
if([name isEqualToString:@"cuesheet"]) {
_cuesheet = value;
flacDecoder->cuesheetFound = YES;
} else if([name isEqualToString:@"waveformatextensible_channel_mask"]) {
if([value hasPrefix:@"0x"]) {
char *end;
const char *_value = [value UTF8String] + 2;
flacDecoder->channelConfig = (uint32_t)strtoul(_value, &end, 16);
@autoreleasepool {
NSString *name = guess_encoding_of_string(_name);
NSString *value = guess_encoding_of_string(_value);
free(_name);
free(_value);
name = [name lowercaseString];
name = [flacDecoder->dataStore coalesceString:name];
value = [flacDecoder->dataStore coalesceString:value];
if([name isEqualToString:@"cuesheet"]) {
_cuesheet = value;
flacDecoder->cuesheetFound = YES;
} else if([name isEqualToString:@"waveformatextensible_channel_mask"]) {
if([value hasPrefix:@"0x"]) {
char *end;
const char *_value = [value UTF8String] + 2;
flacDecoder->channelConfig = (uint32_t)strtoul(_value, &end, 16);
}
} else if([name isEqualToString:@"unsynced lyrics"] ||
[name isEqualToString:@"lyrics"]) {
setDictionary(_metaDict, @"unsyncedlyrics", value);
} else {
setDictionary(_metaDict, name, value);
}
} else if([name isEqualToString:@"unsynced lyrics"] ||
[name isEqualToString:@"lyrics"]) {
setDictionary(_metaDict, @"unsyncedlyrics", value);
} else {
setDictionary(_metaDict, name, value);
}
}
}
Expand Down Expand Up @@ -308,6 +314,9 @@ - (BOOL)open:(id<CogSource>)s {
cuesheetFound = NO;
cuesheet = @"";

id dataStoreClass = NSClassFromString(@"RedundantPlaylistDataStore");
dataStore = [[dataStoreClass alloc] init];

decoder = FLAC__stream_decoder_new();
if(decoder == NULL)
return NO;
Expand Down
Loading

0 comments on commit 8c67b6c

Please sign in to comment.