在一些iOS设备上,用于检索音频文件元数据的Swift代码无法正常工作。

huangapple go评论65阅读模式
英文:

Swift code to retrieve audio file metadata is not working on some iOS devices

问题

以下是Swift代码的中文翻译:

考虑以下的Swift代码,用于检索iOS上音频文件的元数据(标题、专辑名称、艺术家、封面)。它通过给定目录中的文件进行迭代,并使用 AVAsset 提取元数据。

这段代码在运行iOS v16.3或更低版本的物理和虚拟设备上按预期工作。然而,在我的iPhone 13 Pro Max上运行iOS v16.3.1时,所有元数据都返回null。不过,歌曲的URI(songURI)正常工作(证明是我可以使用AVAudioPlayer正常播放这些歌曲的)。

我不知道是什么原因导致了这个问题。我无法在任何其他物理设备上进行测试(我没有其他设备),而且我的MacBook有点过时,所以无法运行更新的iOS版本(如16.5),因为受到Xcode和无法升级的macOS Monterey的限制。

可能的原因是什么?

do {
    // 获取目录内容的URL(包括子目录的URL)
    let directoryContents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)
    
    for songurl in directoryContents {
        print(songurl.localizedName ?? songurl.lastPathComponent)
        
        if isValidAudioFileExtension(songurl.pathExtension) {
            let shouldStopAccessing = songurl.startAccessingSecurityScopedResource()
            
            defer {
                if shouldStopAccessing {
                    songurl.stopAccessingSecurityScopedResource()
                }
            }
            
            let asset = AVAsset(url: songurl)
            let metaData = asset.metadata
            
            let title = metaData.first(where: {$0.commonKey == .commonKeyTitle})?.value as? String
            let artist = metaData.first(where: {$0.commonKey == .commonKeyArtist})?.value as? String
            let album = metaData.first(where: {$0.commonKey == .commonKeyAlbumName})?.value as? String
            let playbackDuration: Double? = CMTimeGetSeconds(asset.duration)
            var artwork: MPMediaItemArtwork? = nil
            if let artworkdata = metaData.first(where: {$0.commonKey == .commonKeyArtwork}), let value = artworkdata.value as? Data {
                artwork = MPMediaItemArtwork(boundsSize: CGSize(width: 60, height: 60), requestHandler: { (size) -> UIImage in
                    return UIImage(data: value)!
                })
            }

            do {
                let bookmarkData = try songurl.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil)
                var isStale = false
                let bookmarkedURL = try URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &isStale)
                
                print(bookmarkedURL)
                
                let song = SongItem(
                    url: bookmarkedURL,
                    title: title ?? "Undefined Song",
                    artist: artist,
                    albumTitle: album,
                    playbackDuration: Int(playbackDuration ?? 0),
                    artwork: artwork
                )
                
                self.tracklist.append(song)
            } catch {
                print("Failed to create bookmark data for URL: \(error.localizedDescription)")
                showToast(message: "\(error.localizedDescription)")
            }
        }
    }
} catch {
    print(error)
}

注意:我也尝试过移除安全范围访问块(startAccessingSecurityScopedResource),但没有任何区别。

英文:

Consider the following Swift code to retrieve audio file metadata (title, album name, artist, artwork) on iOS. It iterates through files in a given directory and extracts metadata using AVAsset

This code works as expected on physical and virtual devices running iOS v16.3 or lower. However, on my iPhone 13 Pro Max running iOS v16.3.1, all metadata returns null. The songURI however, works just fine (and the proof to that is that I can play the songs normally using AVAudioPlayer using songuri

I have no idea what could be causing this. I cannot test this on any other physical device (I do not own any other devices) and also, my MacBook is a bit outdated so I cannot run newer iOS versions (such as 16.5) due to the limitation of Xcode and macOS Monterey which I cannot upgrade)

What could be the reason ?

do {
				// Get the directory contents urls (including subfolders urls)
				let directoryContents = try FileManager.default.contentsOfDirectory(at: url,includingPropertiesForKeys: nil)
				
				for songurl in directoryContents {
					print(songurl.localizedName ?? songurl.lastPathComponent)
					
					if isValidAudioFileExtension(songurl.pathExtension) {
						let shouldStopAccessing = songurl.startAccessingSecurityScopedResource()
						
						defer {
							if shouldStopAccessing {
								songurl.stopAccessingSecurityScopedResource()
							}
						}
						
						let asset = AVAsset(url: songurl)
						let metaData = asset.metadata
						
						let title = metaData.first(where: {$0.commonKey == .commonKeyTitle})?.value as? String
						let artist = metaData.first(where: {$0.commonKey == .commonKeyArtist})?.value as? String
						let album = metaData.first(where: {$0.commonKey == .commonKeyAlbumName})?.value as? String
						let playbackDuration: Double? = CMTimeGetSeconds(asset.duration)
						var artwork: MPMediaItemArtwork? = nil
						if let artworkdata = metaData.first(where: {$0.commonKey == .commonKeyArtwork}), let value = artworkdata.value as? Data {
							artwork = MPMediaItemArtwork(boundsSize: CGSize(width: 60, height: 60), requestHandler: { (size) -> UIImage in
								return UIImage(data: value)!
							})
						}

						do {
							let bookmarkData = try songurl.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil)
							var isStale = false
							let bookmarkedURL = try URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &isStale)
						
							print(bookmarkedURL)
							
							let song = SongItem(
								url: bookmarkedURL,
								title: title ?? "Undefined Song",
								artist: artist,
								albumTitle: album,
								playbackDuration: Int(playbackDuration ?? 0),
								artwork: artwork
							)
							
							self.tracklist.append(song)
						} catch {
							print("Failed to create bookmark data for URL: \(error.localizedDescription)")
							showToast(message: "\(error.localizedDescription)")
						}
					}
				}
			} catch {
				print(error)
			}

NOTE: I also tried removing the security-scoped access block (```startAccessingSecurityScopedResource``) but no difference.

答案1

得分: 0

是设备特定的错误导致了意外行为,不仅影响了媒体元数据的检索,还影响了整个AV框架。在将设备升级到iOS 16.5后,代码开始按预期工作。

英文:

Turns out that it's a device-specific bug leading to unexpected behavior, which not only affected media metadata retrieval but also the entirety of AV framework. Code began to work as expected after upgrading device to iOS 16.5.

huangapple
  • 本文由 发表于 2023年6月25日 17:32:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76549748.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定