FFmpeg input: Support reading metadata

Where TagLib is not being employed, use FFmpeg to read tags where
possible. This allows reading tags from files like IFF. It reads it
through properties, otherwise allowing tag readers to function like
usual.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
CQTexperiment
Christopher Snowhill 2022-02-10 15:29:13 -08:00
parent 5ff1f95481
commit 39dcb88728
5 changed files with 116 additions and 18 deletions

View File

@ -66,6 +66,8 @@
8384912718080FF100E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384912618080FF100E7332D /* Logging.h */; };
839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; };
839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; };
8399CF2C27B5D1D5008751F1 /* NSDictionary+Merge.h in Headers */ = {isa = PBXBuildFile; fileRef = 8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */; };
8399CF2D27B5D1D5008751F1 /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399CF2B27B5D1D4008751F1 /* NSDictionary+Merge.m */; };
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */; settings = {ATTRIBUTES = (Public, ); }; };
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E8D3D2E0CBAEE6E00135C1B /* AudioContainer.m */; };
@ -160,6 +162,8 @@
8384912618080FF100E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
839366651815923C006DD712 /* CogPluginMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogPluginMulti.h; sourceTree = "<group>"; };
839366661815923C006DD712 /* CogPluginMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogPluginMulti.m; sourceTree = "<group>"; };
8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+Merge.h"; path = "../../Utils/NSDictionary+Merge.h"; sourceTree = "<group>"; };
8399CF2B27B5D1D4008751F1 /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Merge.m"; path = "../../Utils/NSDictionary+Merge.m"; sourceTree = "<group>"; };
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8DC2EF5B0486A6940098B216 /* CogAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CogAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContainer.h; sourceTree = "<group>"; };
@ -333,6 +337,8 @@
17D21CDC0B8BE5B400D1EBDE /* Utils */ = {
isa = PBXGroup;
children = (
8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */,
8399CF2B27B5D1D4008751F1 /* NSDictionary+Merge.m */,
8347C73F2796C58800FA8A7D /* NSFileHandle+CreateFile.h */,
8347C7402796C58800FA8A7D /* NSFileHandle+CreateFile.m */,
8384912618080FF100E7332D /* Logging.h */,
@ -430,6 +436,7 @@
17D21CA10B8BE4BA00D1EBDE /* BufferChain.h in Headers */,
17D21CA50B8BE4BA00D1EBDE /* InputNode.h in Headers */,
17D21CA70B8BE4BA00D1EBDE /* Node.h in Headers */,
8399CF2C27B5D1D5008751F1 /* NSDictionary+Merge.h in Headers */,
17D21CA90B8BE4BA00D1EBDE /* OutputNode.h in Headers */,
17D21CC50B8BE4BA00D1EBDE /* OutputCoreAudio.h in Headers */,
834FD4F427AFA2150063BC83 /* Downmix.h in Headers */,
@ -536,6 +543,7 @@
835EDD7B279FE23A001EDCCE /* HeadphoneFilter.m in Sources */,
17D21CA20B8BE4BA00D1EBDE /* BufferChain.m in Sources */,
17D21CA60B8BE4BA00D1EBDE /* InputNode.m in Sources */,
8399CF2D27B5D1D5008751F1 /* NSDictionary+Merge.m in Sources */,
17D21CA80B8BE4BA00D1EBDE /* Node.m in Sources */,
17D21CAA0B8BE4BA00D1EBDE /* OutputNode.m in Sources */,
834FD4F527AFA2150063BC83 /* Downmix.m in Sources */,

View File

@ -6,6 +6,8 @@
#import "NSFileHandle+CreateFile.h"
#import "NSDictionary+Merge.h"
@implementation PluginController
@synthesize sources;
@ -535,10 +537,11 @@ static PluginController *sharedPluginController = nil;
}
NSDictionary *properties = [decoder properties];
NSDictionary *metadata = [decoder metadata];
[decoder close];
return properties;
return [NSDictionary dictionaryByMerging:properties with:metadata];
}
}

View File

@ -371,7 +371,7 @@
@dynamic albumArt;
- (NSImage *)albumArt {
if(!albumArtInternal) return nil;
if(!albumArtInternal || ![albumArtInternal length]) return nil;
NSString *imageCacheTag = [NSString stringWithFormat:@"%@-%@-%@-%@", album, artist, genre, year];
NSImage *image = [NSImage imageNamed:imageCacheTag];

View File

@ -47,10 +47,22 @@
BOOL endOfAudio;
int metadataIndex;
NSString *genre;
NSString *artist;
NSString *title;
NSString *albumartist;
NSString *album;
NSString *title;
NSString *genre;
NSNumber *year;
NSNumber *track;
NSNumber *disc;
float replayGainAlbumGain;
float replayGainAlbumPeak;
float replayGainTrackGain;
float replayGainTrackPeak;
int attachedPicIndex;
NSData *albumArt;
NSDictionary *id3Metadata;
}

View File

@ -157,6 +157,7 @@ static uint8_t reverse_bits[0x100];
streamIndex = -1;
metadataIndex = -1;
attachedPicIndex = -1;
AVCodecParameters *codecPar;
for(i = 0; i < formatCtx->nb_streams; i++) {
@ -167,6 +168,8 @@ static uint8_t reverse_bits[0x100];
streamIndex = i;
} else if(codecPar->codec_id == AV_CODEC_ID_TIMED_ID3) {
metadataIndex = i;
} else if(stream->disposition & AV_DISPOSITION_ATTACHED_PIC) {
attachedPicIndex = i;
} else {
stream->discard = AVDISCARD_ALL;
}
@ -445,10 +448,19 @@ static uint8_t reverse_bits[0x100];
seekable = [s seekable];
genre = @"";
album = @"";
artist = @"";
albumartist = @"";
album = @"";
title = @"";
genre = @"";
year = @(0);
track = @(0);
disc = @(0);
replayGainAlbumGain = 0.0;
replayGainAlbumPeak = 0.0;
replayGainTrackGain = 0.0;
replayGainTrackPeak = 0.0;
albumArt = [NSData data];
id3Metadata = @{};
[self updateMetadata];
@ -495,13 +507,19 @@ static uint8_t reverse_bits[0x100];
}
- (void)updateMetadata {
if([source seekable]) return;
const AVDictionaryEntry *tag = NULL;
NSString *_genre = genre;
NSString *_album = album;
NSString *_artist = artist;
NSString *_albumartist = albumartist;
NSString *_album = album;
NSString *_title = title;
NSString *_genre = genre;
NSNumber *_year = year;
NSNumber *_track = track;
NSNumber *_disc = disc;
float _replayGainAlbumGain = replayGainAlbumGain;
float _replayGainAlbumPeak = replayGainAlbumPeak;
float _replayGainTrackGain = replayGainTrackGain;
float _replayGainTrackPeak = replayGainTrackPeak;
if(formatCtx->metadata) {
while((tag = av_dict_get(formatCtx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
if(!strcasecmp(tag->key, "streamtitle")) {
@ -517,10 +535,35 @@ static uint8_t reverse_bits[0x100];
_album = [NSString stringWithUTF8String:tag->value];
} else if(!strcasecmp(tag->key, "icy-genre")) {
_genre = [NSString stringWithUTF8String:tag->value];
} else if(!strcasecmp(tag->key, "album")) {
_album = [NSString stringWithUTF8String:tag->value];
} else if(!strcasecmp(tag->key, "album_artist")) {
_albumartist = [NSString stringWithUTF8String:tag->value];
} else if(!strcasecmp(tag->key, "artist")) {
_artist = [NSString stringWithUTF8String:tag->value];
} else if(!strcasecmp(tag->key, "title")) {
_title = [NSString stringWithUTF8String:tag->value];
} else if(!strcasecmp(tag->key, "date")) {
NSString *dateString = [NSString stringWithUTF8String:tag->value];
_year = @([dateString intValue]);
} else if(!strcasecmp(tag->key, "track")) {
NSString *trackString = [NSString stringWithUTF8String:tag->value];
_track = @([trackString intValue]);
} else if(!strcasecmp(tag->key, "disc")) {
NSString *discString = [NSString stringWithUTF8String:tag->value];
_disc = @([discString intValue]);
} else if(!strcasecmp(tag->key, "replaygain_album_gain")) {
NSString *rgValue = [NSString stringWithUTF8String:tag->value];
_replayGainAlbumGain = [rgValue floatValue];
} else if(!strcasecmp(tag->key, "replaygain_album_peak")) {
NSString *rgValue = [NSString stringWithUTF8String:tag->value];
_replayGainAlbumPeak = [rgValue floatValue];
} else if(!strcasecmp(tag->key, "replaygain_track_gain")) {
NSString *rgValue = [NSString stringWithUTF8String:tag->value];
_replayGainTrackGain = [rgValue floatValue];
} else if(!strcasecmp(tag->key, "replaygain_track_peak")) {
NSString *rgValue = [NSString stringWithUTF8String:tag->value];
_replayGainTrackPeak = [rgValue floatValue];
}
}
}
@ -537,16 +580,34 @@ static uint8_t reverse_bits[0x100];
}
}
if(![_genre isEqual:genre] ||
if(![_artist isEqual:artist] ||
![_albumartist isEqual:albumartist] ||
![_album isEqual:album] ||
![_artist isEqual:artist] ||
![_title isEqual:title]) {
genre = _genre;
album = _album;
![_title isEqual:title] ||
![_genre isEqual:genre] ||
![_year isEqual:year] ||
![_track isEqual:track] ||
![_disc isEqual:disc] ||
_replayGainAlbumGain != replayGainAlbumGain ||
_replayGainAlbumPeak != replayGainAlbumPeak ||
_replayGainTrackGain != replayGainTrackGain ||
_replayGainTrackPeak != replayGainTrackPeak) {
artist = _artist;
albumartist = _albumartist;
album = _album;
title = _title;
[self willChangeValueForKey:@"metadata"];
[self didChangeValueForKey:@"metadata"];
genre = _genre;
year = _year;
track = _track;
disc = _disc;
replayGainAlbumGain = _replayGainAlbumGain;
replayGainAlbumPeak = _replayGainAlbumPeak;
replayGainTrackGain = _replayGainTrackGain;
replayGainTrackPeak = _replayGainTrackPeak;
if(![source seekable]) {
[self willChangeValueForKey:@"metadata"];
[self didChangeValueForKey:@"metadata"];
}
}
}
@ -563,6 +624,17 @@ static uint8_t reverse_bits[0x100];
}
}
- (void)updateArtwork {
NSData *_albumArt = [NSData dataWithBytes:lastReadPacket->data length:lastReadPacket->size];
if(![_albumArt isEqual:albumArt]) {
albumArt = _albumArt;
if(![source seekable]) {
[self willChangeValueForKey:@"metadata"];
[self didChangeValueForKey:@"metadata"];
}
}
}
- (int)readAudio:(void *)buf frames:(UInt32)frames {
if(totalFrames && framesRead >= totalFrames)
return 0;
@ -611,6 +683,9 @@ static uint8_t reverse_bits[0x100];
if(lastReadPacket->stream_index == metadataIndex) {
[self updateID3Metadata];
continue;
} else if(lastReadPacket->stream_index == attachedPicIndex) {
[self updateArtwork];
continue;
}
if(lastReadPacket->stream_index != streamIndex)
@ -832,7 +907,7 @@ static uint8_t reverse_bits[0x100];
}
- (NSDictionary *)metadata {
return [NSDictionary dictionaryByMerging:@{ @"genre": genre, @"album": album, @"artist": artist, @"title": title } with:id3Metadata];
return [NSDictionary dictionaryByMerging:@{ @"artist": artist, @"albumartist": albumartist, @"album": album, @"title": title, @"genre": genre, @"year": year, @"track": track, @"disc": disc, @"replayGainAlbumGain": @(replayGainAlbumGain), @"replayGainAlbumPeak": @(replayGainAlbumPeak), @"replayGainTrackGain": @(replayGainTrackGain), @"replayGainTrackPeak": @(replayGainTrackPeak), @"albumArt": albumArt } with:id3Metadata];
}
+ (NSArray *)fileTypes {