【问题标题】:how to fetch the details of the audio file in iPhone如何在 iPhone 中获取音频文件的详细信息
【发布时间】:2012-01-04 23:12:43
【问题描述】:

我使用 AVAudioPlayer 制作了自定义播放器。现在,我想获取添加到资源文件夹中的音频文件的详细信息,例如艺术家姓名、专辑名称等。

MPMusicPlayer 提供了获取详细信息的 API,但它使用 iPod 库并且它不从应用程序的沙箱中获取资源。所以,MPMusicPlayer 在那种情况下是行不通的。

那么,我们如何在 iPhone 中获取音频文件的详细信息。

【问题讨论】:

    标签: iphone objective-c ipod id3 albumart


    【解决方案1】:

    您可以通过AudioToolbox.framework 获取此信息。 AudioToolbox.framework 是一个 C API,所以我为它编写了一个 Objective-C 包装器:

    ID3Tag .h:

    @interface ID3Tag : NSObject <NSCoding> {
        NSString* title_;
        NSString* album_;
        NSString* artist_;
        NSNumber* trackNumber_;
        NSNumber* totalTracks_;
        NSString* genre_;
        NSString* year_;
        NSNumber* approxDuration_;
        NSString* composer_;
        NSString* tempo_;
        NSString* keySignature_;
        NSString* timeSignature_;
        NSString* lyricist_;
        NSString* recordedDate_;
        NSString* comments_;
        NSString* copyright_;
        NSString* sourceEncoder_;
        NSString* encodingApplication_;
        NSString* bitRate_;
        NSStream* sourceBitRate_;
        NSString* channelLayout_;
        NSString* isrc_;
        NSString* subtitle_;  
    }
    
    @property (nonatomic, retain) NSString *title;
    @property (nonatomic, retain) NSString *album;
    @property (nonatomic, retain) NSString *artist;
    @property (nonatomic, retain) NSNumber *trackNumber;
    @property (nonatomic, retain) NSNumber *totalTracks;
    @property (nonatomic, retain) NSString *genre;
    @property (nonatomic, retain) NSString *year;
    @property (nonatomic, retain) NSNumber *approxDuration;
    @property (nonatomic, retain) NSString *composer;
    @property (nonatomic, retain) NSString *tempo;
    @property (nonatomic, retain) NSString *keySignature;
    @property (nonatomic, retain) NSString *timeSignature;
    @property (nonatomic, retain) NSString *lyricist;
    @property (nonatomic, retain) NSString *recordedDate;
    @property (nonatomic, retain) NSString *comments;
    @property (nonatomic, retain) NSString *copyright;
    @property (nonatomic, retain) NSString *sourceEncoder;
    @property (nonatomic, retain) NSString *encodingApplication;
    @property (nonatomic, retain) NSString *bitRate;
    @property (nonatomic, retain) NSStream *sourceBitRate;
    @property (nonatomic, retain) NSString *channelLayout;
    @property (nonatomic, retain) NSString *isrc;
    @property (nonatomic, retain) NSString *subtitle;
    
    @end
    

    ID3TagParser.h

    #import <Foundation/Foundation.h>
    #import "ID3Tag.h"
    
    @interface ID3Parser : NSObject {
    
    }
    
    - (ID3Tag*) parseAudioFileForID3Tag:(NSURL*) url;
    
    @end
    

    ID3TagParser.m

    #import "ID3Parser.h"
    #import <AudioToolbox/AudioToolbox.h>
    
    @implementation ID3Parser
    
    - (ID3Tag*) parseAudioFileForID3Tag:(NSURL*) url {
        if (url == nil) {
            return nil;
        }
    
        AudioFileID fileID  = nil;
        OSStatus err = noErr;
    
        err = AudioFileOpenURL( (CFURLRef) url, kAudioFileReadPermission, 0, &fileID );
        if( err != noErr ) {
            NSLog( @"AudioFileOpenURL failed" );
            return nil;
        } else {
            UInt32 id3DataSize = 0;
            char* rawID3Tag = NULL;
    
            //  Reads in the raw ID3 tag info
            err = AudioFileGetPropertyInfo(fileID, kAudioFilePropertyID3Tag, &id3DataSize, NULL);
            if(err != noErr) {
                return nil;
            }
    
            //  Allocate the raw tag data
            rawID3Tag = (char *) malloc(id3DataSize);
    
            if(rawID3Tag == NULL) {
                return nil;
            }
    
            err = AudioFileGetProperty(fileID, kAudioFilePropertyID3Tag, &id3DataSize, rawID3Tag);
            if(err != noErr) {
                return nil;
            }
    
            UInt32 id3TagSize = 0;
            UInt32 id3TagSizeLength = 0;
            err = AudioFormatGetProperty(kAudioFormatProperty_ID3TagSize, id3DataSize, rawID3Tag, &id3TagSizeLength, &id3TagSize);
    
            if(err != noErr) {
                switch(err) {
                    case kAudioFormatUnspecifiedError:
                        NSLog(@"err: audio format unspecified error");
                        return nil;
                    case kAudioFormatUnsupportedPropertyError:
                        NSLog(@"err: audio format unsupported property error");
                        return nil;
                    case kAudioFormatBadPropertySizeError:
                        NSLog(@"err: audio format bad property size error"); 
                        return nil;
                    case kAudioFormatBadSpecifierSizeError:
                        NSLog(@"err: audio format bad specifier size error"); 
                        return nil;
                    case kAudioFormatUnsupportedDataFormatError:
                        NSLog(@"err: audio format unsupported data format error"); 
                        return nil;
                    case kAudioFormatUnknownFormatError:
                        NSLog(@"err: audio format unknown format error");
                        return nil;
                    default:
                        NSLog(@"err: some other audio format error"); 
                        return nil;
                }
            }
    
            CFDictionaryRef piDict = nil;
            UInt32 piDataSize = sizeof(piDict);
    
            //  Populates a CFDictionary with the ID3 tag properties
            err = AudioFileGetProperty(fileID, kAudioFilePropertyInfoDictionary, &piDataSize, &piDict);
            if(err != noErr) {
                NSLog(@"AudioFileGetProperty failed for property info dictionary");
                return nil;
            }
    
            //  Toll free bridge the CFDictionary so that we can interact with it via objc
            NSDictionary* nsDict = (NSDictionary*)piDict;
    
            ID3Tag* tag = [[[ID3Tag alloc] init] autorelease];
    
            tag.album = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Album]];
            tag.approxDuration = [NSNumber numberWithInt:[[nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_ApproximateDurationInSeconds]] intValue]];
            tag.artist = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Artist]];
            tag.bitRate = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_NominalBitRate]];
            tag.channelLayout = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_ChannelLayout]];
            tag.comments = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Comments]];
            tag.composer = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Composer]];
            tag.copyright = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Copyright]];
            tag.encodingApplication = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_EncodingApplication]];
            tag.genre = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Genre]];
            tag.isrc = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_ISRC]];
            tag.keySignature = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_KeySignature]];
            tag.lyricist = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Lyricist]];
            tag.recordedDate = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_RecordedDate]];
            tag.sourceBitRate = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_SourceBitDepth]];
            tag.sourceEncoder = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_SourceEncoder]];
            tag.subtitle = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_SubTitle]];
            tag.tempo = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Tempo]];
            tag.timeSignature = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_TimeSignature]];
            tag.title = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Title]];
            tag.year = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Year]];
    
            /*  
             *  We're going to parse tracks differently so that we can perform queries on the data. This means we need to look
             *  for a '/' so that we can seperate out the track from the total tracks on the source compilation (if it's there).
             */
            NSString* tracks = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_TrackNumber]];
    
            int slashLocation = [tracks rangeOfString:@"/"].location;
    
            if (slashLocation == NSNotFound) {
                tag.trackNumber = [NSNumber numberWithInt:[tracks intValue]];
            } else {
                tag.trackNumber = [NSNumber numberWithInt:[[tracks substringToIndex:slashLocation] intValue]];
                tag.totalTracks = [NSNumber numberWithInt:[[tracks substringFromIndex:(slashLocation+1 < [tracks length] ? slashLocation+1 : 0 )] intValue]];
            }
    
            //  ALWAYS CLEAN UP!
            CFRelease(piDict);
            nsDict = nil;
            free(rawID3Tag);
    
            return tag;
        }
    }
    
    @end
    

    【讨论】:

    • 不错的代码!你只是忘记了一些内存清理(最重要的是AudioFileClose)。我在一堆文件上运行了代码,通常内存问题不会是问题。
    • 我可以得到赛道的所有信息。但无法得到歌词......所以关于歌词的任何想法......
    • 我可以得到正在播放的声音文件的歌词(文本)吗?
    • @NiravPatel 我会在运行时调试nsDict 变量,看看它是否包含访问该元数据的密钥。
    • @WayneHartman :非常感谢朋友。我期待着您的回答。
    【解决方案2】:

    我看到标签读取代码Wayne posts 被复制粘贴太多。 如果使用kAudioFilePropertyInfoDictionary,则不需要 id3DataSize,不需要读取原始 ID3Tag 数据,并且 id3TagSizeLength id3TagSize 也不使用。只需打开文件并读取标签:

    - (NSDictionary *)id3TagsForURL:(NSURL *)resourceUrl
    {
        AudioFileID fileID;
        OSStatus result = AudioFileOpenURL((CFURLRef)resourceUrl, kAudioFileReadPermission, 0, &fileID);
    
        if (result != noErr) {
            NSLog(@"Error reading tags: %li", result);
            return nil;
        }
    
        CFDictionaryRef piDict = nil;
        UInt32 piDataSize = sizeof(piDict);
    
        result = AudioFileGetProperty(fileID, kAudioFilePropertyInfoDictionary, &piDataSize, &piDict);
        if (result != noErr)
            NSLog(@"Error reading tags. AudioFileGetProperty failed");
    
        AudioFileClose(fileID);
    
        NSDictionary *tagsDictionary = [NSDictionary dictionaryWithDictionary:(NSDictionary*)piDict];
        CFRelease(piDict);
    
        return tagsDictionary;
    }
    

    字典键在 AudioFile.h 中定义,以 kAFInfoDictionary 开头。

    但是,还有另一个标签读取键,它会返回不同的结果(与其他键):

    - (NSDictionary *)id3TagsForURL:(NSURL *)resourceUrl
    {
        AudioFileID fileID;
        OSStatus result = AudioFileOpenURL((CFURLRef)resourceUrl, kAudioFileReadPermission, 0, &fileID);
    
        if (result != noErr) {
            return nil;
        }
    
        //read raw ID3Tag size
        UInt32 id3DataSize = 0;
        char *rawID3Tag = NULL;
        result = AudioFileGetPropertyInfo(fileID, kAudioFilePropertyID3Tag, &id3DataSize, NULL);
        if (result != noErr) {
            AudioFileClose(fileID);
            return nil;
        }
    
        rawID3Tag = (char *)malloc(id3DataSize);
    
        //read raw ID3Tag
        result = AudioFileGetProperty(fileID, kAudioFilePropertyID3Tag, &id3DataSize, rawID3Tag);
        if (result != noErr) {
            free(rawID3Tag);
            AudioFileClose(fileID);
            return nil;
        }
    
        CFDictionaryRef piDict = nil;
        UInt32 piDataSize = sizeof(piDict);
    
        //this key returns some other dictionary, which works also in iPod library
        result = AudioFormatGetProperty(kAudioFormatProperty_ID3TagToDictionary, id3DataSize, rawID3Tag, &piDataSize, &piDict);
        if (result != noErr) {
            return nil;
        }
    
        free(rawID3Tag);
        AudioFileClose(fileID);
    
        NSDictionary *tagsDictionary = [NSDictionary dictionaryWithDictionary:(NSDictionary*)piDict];
        CFRelease(piDict);
    
        return tagsDictionary;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多