i'm trying to add audio file selected with MPMediaPickerController to video with AVMutableVideoComposition, but it gives error and doesn't work.
picking audio like this :
func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
if let musicUrl: NSURL = mediaItemCollection.items.first?.assetURL as NSURL? {
musicURL = musicUrl as URL
print("Music URL in did select \(musicUrl) !!!")
}
}
and mixing audio and video like this :
func testMerge(url: URL) {
let firstVideo = AVAsset(url: url)
var audioAsset = AVAsset(url: musicURL!)
print("Music URL\(musicURL)")
let firstVideoTrack = firstVideo.tracks(withMediaType: AVMediaType.video).first
let mixComposition = AVMutableComposition()
let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
let firstTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
do {
try firstTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: firstVideo.duration), of: firstVideoTrack!, at: .zero)
if musicURL.isFileURL {
try audioTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: firstVideo.duration), of: audioAsset.tracks(withMediaType: AVMediaType.audio).first, at: .zero)
}
} catch {
debugPrint("Can't get track from the Video URL!")
}
let mainInstruction = AVMutableVideoCompositionInstruction()
mainInstruction.timeRange = CMTimeRangeMake(start: .zero, duration: firstVideo.duration)
let firstLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: firstTrack!)
let scale = CGAffineTransform(scaleX: 1, y: 1)
firstLayerInstruction.setTransform(scale, at: .zero)
mainInstruction.layerInstructions = [firstLayerInstruction]
let mainCompositionInstruction = AVMutableVideoComposition()
mainCompositionInstruction.instructions = [mainInstruction]
mainCompositionInstruction.frameDuration = CMTimeMake(value: 1, timescale: 30)
mainCompositionInstruction.renderSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
guard let documentDirectory = FileManager.default.urls (
for: .documentDirectory,
in: .userDomainMask).first else { print("ERROR"); return }
let url = documentDirectory.appendingPathComponent("mergeVideo-\(arc4random() % 10000).mp4")
guard let assetExport = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else {print("ERROR"); return }
assetExport.videoComposition = mainCompositionInstruction
assetExport.outputFileType = .mp4
assetExport.shouldOptimizeForNetworkUse = true
assetExport.outputURL = url
assetExport.exportAsynchronously {
switch assetExport.status {
case AVAssetExportSessionStatus.failed:
print("failed \(String(describing: assetExport.error))")
case AVAssetExportSessionStatus.cancelled:
print("cancelled \(String(describing: assetExport.error))")
case AVAssetExportSessionStatus.completed:
print("Completed")
print(url)
default:
print("unknown")
}
DispatchQueue.main.async {
self.exportDidFinish(assetExport)
}
}
}
if i play that audio with AVAudioPlayer then it is playing but if i add in video it gives error {Error Domain=NSOSStatusErrorDomain Code=-12109 "(null)"}, NSLocalizedFailureReason=The operation is not supported for this media., NSLocalizedDescription=Operation Stopped}
any help or tip would be really appreciated, thank you.
If i add audio file to project directory and then access it with bundle url then it works fine it just doesn't work with audio picked through MPMediaPicker.
This is how i was able to make it work :
stored it as AVPlayerItem in variable like this
var playerItem: AVPlayerItem!
func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
if let musicUrl: NSURL = mediaItemCollection.items.first?.assetURL as NSURL? {
musicURL = musicUrl as URL
print("Music URL in did select \(musicUrl) !!!")
}
playerItem = AVPlayerItem(url: musicURL)
}
then in mergeVideo function accessed it's track like this
do {
try firstTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: durationTrack.duration), of: firstVideoTrack!, at: .zero)
try secondTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: durationTrack.duration), of: secondVideoTrack!, at: .zero)
if playerItem != nil {
let sourceAudio = playerItem.asset.tracks(withMediaType: .audio).first
let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
try audioTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: durationTrack.duration), of: sourceAudio!, at: .zero)
}
} catch {
debugPrint("Can't get track from the Video URL!")
}