AVAssetWriter fails only in iOS when writing audio from specific videos

574 views Asked by At

I have a sample project for resizing videos that works well for most videos. However, AVAssetWriter fails to write the audio from specific videos with the error:

Error Domain=AVFoundationErrorDomain 
Code=-11800 "The operation could not be completed" 
UserInfo={
  NSLocalizedFailureReason=An unknown error occurred (-12780), 
  NSLocalizedDescription=The operation could not be completed, 
  NSUnderlyingError=0x282e956e0 {
    Error Domain=NSOSStatusErrorDomain Code=-12780 "(null)"
  }
}

What is even more problematic is that the same code works fine if I run it on macOS, but it breaks in iOS. I think it isn't a hardware problem because it also breaks in the iOS simulator.

These are the settings I use for (de)compressing the asset tracks:

func audioDecompressionSettings() -> [String: Any] {
    return [
        AVFormatIDKey: kAudioFormatLinearPCM
    ]
}

func audioCompressionSettings() -> [String: Any] {
    var audioChannelLayout = AudioChannelLayout()
    memset(&audioChannelLayout, 0, MemoryLayout<AudioChannelLayout>.size)
    audioChannelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo
    
    return [
        AVFormatIDKey: kAudioFormatMPEG4AAC,
        AVSampleRateKey: 44100,
        AVEncoderBitRateKey: 128000,
        AVNumberOfChannelsKey: 2,
        AVChannelLayoutKey: NSData(bytes: &audioChannelLayout, length: MemoryLayout<AudioChannelLayout>.size)
    ]
}

func videoDecompressionSettings() -> [String: Any] {
    return [
        kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
        kCVPixelBufferMetalCompatibilityKey as String: true
    ]
}

func videoCompressionSettings(size: CGSize) -> [String: Any] {
    return [
        AVVideoCodecKey: AVVideoCodecType.h264,
        AVVideoWidthKey: size.width,
        AVVideoHeightKey: size.height
    ]
}

The complete source code can be found here.

In that project there are two targets, one for Mac and other for iOS, both of them using the same code for resizing the video. I also included two sample video files: fruit.mp4 and rain.mp4. The first one works well in both targets, but the second one breaks in iOS.

Am I missing something here or this is likely to be an Apple bug?

1

There are 1 answers

1
Reynaldo Aguilar On BEST ANSWER

The audio settings for the problematic video are:

Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, 5.1, fltp, 386 kb/s (default)

and for the other one are:

Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 137 kb/s (default)

The important difference between the two is the number of audio channels: 5.1 (5 full bandwidth channels + one low-frequency effects channel) in the first one and stereo (2) in the second one.

When reading the video file, we specify the decompression settings:

[AVFormatIDKey: kAudioFormatLinearPCM]

Which means that the decompressed audio will have the same number of channels as the source file. In our case, we have a 5.1 (actually 6) channels asset and we want to write it to a 2 channels file. It seems that AVAssetWriterInput doesn't handle that case properly in iOS and we get an error.

The solution to the problem is to specify the number of audio channels we want when decompressing the audio from the asset, like this:

[
    AVFormatIDKey: kAudioFormatLinearPCM
    AVNumberOfChannelsKey: 2
]