Swift中播放部分音频文件报错,求兼容3GP等格式的解决方案
Hey there! Let's break down what's happening and fix this audio playback issue with 3GP files in Swift.
The error you're seeing (NSOSStatusErrorDomain Code=2003334207) translates to kAudioFileUnsupportedDataFormatError — this means AVAudioPlayer doesn't natively support 3GP audio containers (or their underlying codecs). AVAudioPlayer has limited format support focused on common consumer formats like MP3, M4A, WAV, etc., so we need to adjust our approach to handle 3GP and other unsupported formats.
Here are two solid solutions depending on your needs:
Solution 1: Switch to AVPlayer (Simplest Fix)
AVPlayer (from the AVFoundation framework) supports a much wider range of media formats, including 3GP, because it leverages Apple's full media playback stack. This is the quickest way to get 3GP files playing without extra work.
Here's how to refactor your function to use AVPlayer:
func setAVPlayer() { // Safely unwrap the filename to avoid force unwraps guard let fileName = URL(string: audioFilename)?.lastPathComponent else { print("Invalid audio filename provided") return } let baseURL = URL(fileURLWithPath: myPath) let audioFileURL = baseURL.appendingPathComponent(fileName) let filePath = audioFileURL.path let fileManager = FileManager.default guard fileManager.fileExists(atPath: filePath) else { print("Audio file not found at path: \(filePath)") return } // Initialize AVPlayer with the file URL let player = AVPlayer(url: audioFileURL) // Update your array to hold AVPlayer instances (adjust the array type to [AVPlayer?]) ChannelViewController.audioPlayerArrayList[indexPathRow] = player }
Note: You'll need to update your audioPlayerArrayList to be an array of AVPlayer? instead of AVAudioPlayer?. To play the audio, call player.play() instead of the AVAudioPlayer play method.
Solution 2: Transcode Unsupported Formats (If You Must Use AVAudioPlayer)
If you have a hard requirement to stick with AVAudioPlayer, we can add logic to transcode unsupported files (like 3GP) to a compatible format (e.g., M4A) before initializing the player.
First, add a helper function to handle transcoding:
// Helper to transcode audio to M4A (a format AVAudioPlayer supports) func transcodeAudio(at sourceURL: URL, to destinationURL: URL, completion: @escaping (Bool, Error?) -> Void) { let audioAsset = AVAsset(url: sourceURL) // Create an export session for M4A output guard let exportSession = AVAssetExportSession(asset: audioAsset, presetName: AVAssetExportPresetAppleM4A) else { let error = NSError(domain: "TranscodingError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to create export session"]) completion(false, error) return } exportSession.outputURL = destinationURL exportSession.outputFileType = .m4a exportSession.exportAsynchronously { switch exportSession.status { case .completed: completion(true, nil) case .failed, .cancelled: completion(false, exportSession.error) default: let error = NSError(domain: "TranscodingError", code: -2, userInfo: [NSLocalizedDescriptionKey: "Unknown export failure"]) completion(false, error) } } }
Then update your original function to check for supported formats and transcode when needed:
func setAVAudioPlayer() { guard let fileName = URL(string: audioFilename)?.lastPathComponent else { print("Invalid audio filename") return } let baseURL = URL(fileURLWithPath: myPath) let sourceFileURL = baseURL.appendingPathComponent(fileName) let filePath = sourceFileURL.path let fileManager = FileManager.default guard fileManager.fileExists(atPath: filePath) else { print("Audio file does not exist at path: \(filePath)") return } // Check if the file format is natively supported by AVAudioPlayer let supportedExtensions = AVAudioPlayer.audioFileTypes() let fileExtension = sourceFileURL.pathExtension.lowercased() if supportedExtensions.contains(fileExtension) { // Load directly with AVAudioPlayer do { let player = try AVAudioPlayer(contentsOf: sourceFileURL) ChannelViewController.audioPlayerArrayList[indexPathRow] = player } catch { print("Error loading audio file:") print(error) } } else { // Transcode to M4A first let transcodedFileName = "\(sourceFileURL.deletingPathExtension().lastPathComponent).m4a" let transcodedFileURL = baseURL.appendingPathComponent(transcodedFileName) transcodeAudio(at: sourceFileURL, to: transcodedFileURL) { [weak self] success, error in guard let self = self else { return } if success { do { let player = try AVAudioPlayer(contentsOf: transcodedFileURL) ChannelViewController.audioPlayerArrayList[self.indexPathRow] = player } catch { print("Error loading transcoded audio file:") print(error) } } else { print("Transcoding failed: \(error?.localizedDescription ?? "Unknown error")") } } } }
Pro Tip: Add caching logic to avoid re-transcoding the same file every time — you can check if the transcoded file exists before starting the transcode process.
内容的提问来源于stack exchange,提问作者Maryam

