avfoundationswift3avassetwriteravassetwriterinput

How to create an outputSettings Dictionary<String. Any> for use with an AVAssetWriterInput instance?


I am attempting to convert a MPMediaItem instance into a caf formatted audio file. I have been following the work of Chris Adamson and his post on From iPod Library to PCM Samples in Far Fewer Steps Than Were Previously Necessary

As I was digging around looking how to do this in swift I came across Abel Domingues github FileConverter.swift for doing just this in Swift.

I then set about converting to Swift 3 as an extension to protocol. All went well until I try to run it it. It crashes on the assetWriterInput object creation and seems to be related to the outputSettings variable.

        var outputSettings = [
            AVFormatIDKey: kAudioFormatLinearPCM,
            AVSampleRateKey: 44100,
            AVNumberOfChannelsKey: 2,
            AVChannelLayoutKey: NSData(bytes:&channelLayout, length:MemoryLayout<AudioChannelLayout>.size),
            AVLinearPCMBitDepthKey: 16,
            AVLinearPCMIsNonInterleaved: false,
            AVLinearPCMIsFloatKey: false,
            AVLinearPCMIsBigEndianKey: false
        ] as [String : Any]

        // create an asset writer input
        let assetWriterInput = AVAssetWriterInput(mediaType:AVMediaTypeAudio, outputSettings:outputSettings as NSDictionary as! [String : Any])

The error message I receive is the following:

-[_SwiftValue unsignedIntValue]: unrecognized selector sent to instance 0x1704407b0 2016-10-13 18:34:52.032784 Testie[3098:1535938] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_SwiftValue unsignedIntValue]: unrecognized selector sent to instance 0x1704407b0'

I have searched for examples of this, but must posts are in Objective-C and/or related to setting up the dictionary for video.

This is the documentation from the AVAssetWriterInput source related to audio:

For AVMediaTypeAudio the following keys are not currently supported in the outputSettings dictionary: AVEncoderAudioQualityKey and AVSampleRateConverterAudioQualityKey. When using this initializer, an audio settings dictionary must be fully specified, meaning that it must contain AVFormatIDKey, AVSampleRateKey, and AVNumberOfChannelsKey. If no other channel layout information is available, a value of 1 for AVNumberOfChannelsKey will result in mono output and a value of 2 will result in stereo output. If AVNumberOfChannelsKey specifies a channel count greater than 2, the dictionary must also specify a value for AVChannelLayoutKey. For kAudioFormatLinearPCM, all relevant AVLinearPCM*Key keys must be included, and for kAudioFormatAppleLossless, AVEncoderBitDepthHintKey keys must be included. See -initWithMediaType:outputSettings:sourceFormatHint: for a way to avoid having to specify a value for each of those keys.

So what in the dictionary is causing the error?


Solution

  • In Swift 3, kAudioFormatLinearPCM is imported as UInt32 (aka AudioFormatID), and Swift 3.0.0 cannot convert it to an appropriate type (NSNumber in this case) when put in [String: Any].

    Try this:

        var outputSettings = [
            AVFormatIDKey: UInt(kAudioFormatLinearPCM),
            AVSampleRateKey: 44100,
            AVNumberOfChannelsKey: 2,
            AVChannelLayoutKey: NSData(bytes:&channelLayout, length:MemoryLayout<AudioChannelLayout>.size),
            AVLinearPCMBitDepthKey: 16,
            AVLinearPCMIsNonInterleaved: false,
            AVLinearPCMIsFloatKey: false,
            AVLinearPCMIsBigEndianKey: false
        ] as [String : Any]
    

    Or wait till Xcode 8.1/Swift 3.0.1, which should fix your case.