Share ringtone to Garageband via UIActivityViewController - swift

I am implementing an ringtone application and I would like to share the audio file to GarageBand to let users continue the process,
Here is the code on how I share the media
fileprivate func shareMedia(url: URL) {
let activityViewController:UIActivityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = saveAsRingtone
activityViewController.popoverPresentationController?.sourceRect = saveAsRingtone.frame
present(activityViewController, animated: true, completion: nil)
}
To Answer my own question here is the full answer
class AudioUtils {
class func trimAudio(audioToTrim: URL, startTime: Double, setExportSession: (AVAssetExportSession) -> Void, completion: #escaping (URL?, String?) -> Void) {
let audioFileInput = audioToTrim
let mixedAudio: String = "ringtone.aif"
let exportPathDirectory = NSTemporaryDirectory()
// let audioFileDirectory = URL(fileURLWithPath: exportPathDirectory).appendingPathComponent("Project").appendingPathExtension("band")
let projectBandDirectory = exportPathDirectory + "Project.band"
if (FileManager.default.fileExists(atPath: projectBandDirectory)) {
try! FileManager.default.removeItem(at: URL(fileURLWithPath: projectBandDirectory))
}
try! FileManager.default.createDirectory(atPath: projectBandDirectory, withIntermediateDirectories: true, attributes: nil)
let exportPath: String = projectBandDirectory + "/"
let bundlePath = Bundle.main.path(forResource: "projectData", ofType: "")
let fullDestPath = NSURL(fileURLWithPath: exportPath).appendingPathComponent("projectData")
let fullDestPathString = (fullDestPath?.path)!
try! FileManager.default.createDirectory(atPath: exportPath + "Media", withIntermediateDirectories: true, attributes: nil)
try! FileManager.default.createDirectory(atPath: exportPath + "Output", withIntermediateDirectories: true, attributes: nil)
let audioFileOutput = URL(fileURLWithPath: exportPathDirectory + mixedAudio)//.appendingPathExtension("band")
print("Will export to \(audioFileOutput.absoluteString)")
try? FileManager.default.removeItem(at: audioFileOutput)
let asset = AVAsset(url: audioFileInput)
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetPassthrough)
if (exportSession == nil) {
completion(nil, "ExportSession is nil")
return
}
setExportSession(exportSession!)
let startCMTime = CMTimeMakeWithSeconds(startTime, preferredTimescale: 1)
let stopCMtime = CMTimeMakeWithSeconds(startTime + 30, preferredTimescale: 1)
let exportTimeRange = CMTimeRangeFromTimeToTime(start: startCMTime, end: stopCMtime)
exportSession?.outputURL = audioFileOutput
exportSession?.outputFileType = AVFileType.caf
exportSession?.timeRange = exportTimeRange
exportSession?.exportAsynchronously {
switch (exportSession?.status) {
case .completed:
var options = AKConverter.Options()
// any options left nil will assume the value of the input file
options.format = "aif"
options.sampleRate = 48000
options.bitDepth = 24
let sourceUrl = audioFileOutput
let destUrl = URL(fileURLWithPath: exportPath + "Media/ringtone.aiff")
convertAudio(sourceUrl, outputURL: destUrl)
let fileData = try! Data.init(contentsOf: destUrl)
var fileStream:String = fileData.base64EncodedString(options: NSData.Base64EncodingOptions.init(rawValue: 0))
fileStream = String(fileStream.dropLast())
let playersDictionary = NSMutableDictionary(contentsOfFile: bundlePath!)
let playersNamesArray = (playersDictionary?.object(forKey: "$objects"))! as! NSMutableArray
let nsDataParentDictionary = (playersNamesArray.object(at: 4)) as! NSMutableDictionary
nsDataParentDictionary.setValue(fileStream, forKey: "NS.data")
playersNamesArray.removeObject(at: 4)
playersNamesArray.insert(nsDataParentDictionary, at: 4)
playersDictionary?.setValue(playersNamesArray, forKey: "$objects")
playersDictionary?.write(toFile: fullDestPathString, atomically: true)
do {
try FileManager.default.copyItem(atPath: bundlePath!, toPath: fullDestPathString)
completion(URL(fileURLWithPath: exportPath), nil)
} catch let exception {
completion(nil, exception.localizedDescription)
}
break
default:
completion(nil, exportSession?.error?.localizedDescription)
break
}
}
}
class func convertAudio(_ url: URL, outputURL: URL) {
var error : OSStatus = noErr
var destinationFile : ExtAudioFileRef? = nil
var sourceFile : ExtAudioFileRef? = nil
var srcFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()
var dstFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()
ExtAudioFileOpenURL(url as CFURL, &sourceFile)
var thePropertySize: UInt32 = UInt32(MemoryLayout.stride(ofValue: srcFormat))
ExtAudioFileGetProperty(sourceFile!,
kExtAudioFileProperty_FileDataFormat,
&thePropertySize, &srcFormat)
dstFormat.mSampleRate = 44100 //Set sample rate
dstFormat.mFormatID = kAudioFormatLinearPCM
dstFormat.mChannelsPerFrame = 1
dstFormat.mBitsPerChannel = 16
dstFormat.mBytesPerPacket = 2 * dstFormat.mChannelsPerFrame
dstFormat.mBytesPerFrame = 2 * dstFormat.mChannelsPerFrame
dstFormat.mFramesPerPacket = 1
dstFormat.mFormatFlags = kAudioFormatFlagIsBigEndian |
kAudioFormatFlagIsSignedInteger
// Create destination file
error = ExtAudioFileCreateWithURL(
outputURL as CFURL,
kAudioFileAIFFType,
&dstFormat,
nil,
AudioFileFlags.eraseFile.rawValue,
&destinationFile)
print("Error = \(error)")
error = ExtAudioFileSetProperty(sourceFile!,
kExtAudioFileProperty_ClientDataFormat,
thePropertySize,
&dstFormat)
print("Error = \(error)")
error = ExtAudioFileSetProperty(destinationFile!,
kExtAudioFileProperty_ClientDataFormat,
thePropertySize,
&dstFormat)
print("Error = \(error)")
let bufferByteSize : UInt32 = 32768
var srcBuffer = [UInt8](repeating: 0, count: 32768)
var sourceFrameOffset : ULONG = 0
while(true){
var fillBufList = AudioBufferList(
mNumberBuffers: 1,
mBuffers: AudioBuffer(
mNumberChannels: 2,
mDataByteSize: UInt32(srcBuffer.count),
mData: &srcBuffer
)
)
var numFrames : UInt32 = 0
if(dstFormat.mBytesPerFrame > 0){
numFrames = bufferByteSize / dstFormat.mBytesPerFrame
}
error = ExtAudioFileRead(sourceFile!, &numFrames, &fillBufList)
print("Error = \(error)")
if(numFrames == 0){
error = noErr;
break;
}
sourceFrameOffset += numFrames
error = ExtAudioFileWrite(destinationFile!, numFrames, &fillBufList)
print("Error = \(error)")
}
error = ExtAudioFileDispose(destinationFile!)
print("Error = \(error)")
error = ExtAudioFileDispose(sourceFile!)
print("Error = \(error)")
}
}
What happens here?...
First of all here is a function to trim the audio for 30 seconds
I have checked an existing .band project file and I got its components, and tried to create these files programmatically. Please feel free to ask me anything about the code
There is projectData file that you should use, this is a file found in every .garageBand folder
https://www.dropbox.com/s/7r7uh2ekzigyy3u/projectData?dl=0

There's no problem with this method for sharing a track on Garageband
Just Make sure that your shared file Extension is "band"
.appendingPathExtension("band") not anything else

Related

Swift Merging audio and video files into one video

I wrote a program in Swift.I want to merge a video with an audio file, but got this error.
"failed Error Domain=AVFoundationErrorDomain Code=-11838 "Operation Stopped" UserInfo=0x17da4230 {NSLocalizedDescription=Operation Stopped, NSLocalizedFailureReason=The operation is not supported for this media.}"
Code
func mergeVideoWithAudio(videoUrl: URL, audioUrl: URL, success: #escaping ((URL) -> Void), failure: #escaping ((Error?) -> Void)) {
let mixComposition: AVMutableComposition = AVMutableComposition()
var mutableCompositionVideoTrack: [AVMutableCompositionTrack] = []
var mutableCompositionAudioTrack: [AVMutableCompositionTrack] = []
let totalVideoCompositionInstruction : AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction()
let aVideoAsset: AVAsset = AVAsset(url: videoUrl)
let aAudioAsset: AVAsset = AVAsset(url: audioUrl)
if let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid), let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) {
mutableCompositionVideoTrack.append(videoTrack)
mutableCompositionAudioTrack.append(audioTrack)
if let aVideoAssetTrack: AVAssetTrack = aVideoAsset.tracks(withMediaType: .video).first, let aAudioAssetTrack: AVAssetTrack = aAudioAsset.tracks(withMediaType: .audio).first {
do {
try mutableCompositionVideoTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aVideoAssetTrack, at: CMTime.zero)
try mutableCompositionAudioTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aAudioAssetTrack, at: CMTime.zero)
videoTrack.preferredTransform = aVideoAssetTrack.preferredTransform
} catch{
print(error)
}
totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero,duration: videoTrack.timeRange.duration)
}
}
let mutableVideoComposition: AVMutableVideoComposition = AVMutableVideoComposition()
mutableVideoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
mutableVideoComposition.renderSize = CGSize(width: 480, height: 640)
if let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let outputURL = URL(fileURLWithPath: documentsPath).appendingPathComponent("\("fileName").mp4")
do {
if FileManager.default.fileExists(atPath: outputURL.path) {
try FileManager.default.removeItem(at: outputURL)
}
} catch { }
// var outputURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("temp_video_data.mp4", isDirectory: false)
// outputURL = URL(fileURLWithPath: outputURL.path)
// let mediaItems = MPMediaQuery.songs().items
// let mediaCollection = MPMediaItemCollection(items: mediaItems ?? [])
// let player1 = MPMusicPlayerController.systemMusicPlayer
// player1.setQueue(with: mediaCollection)
// player1.play()
// guard let url = URL(string: url )else {return}
// let player = AVPlayer(url: url)
// let playerController = AVPlayerViewController()
// playerController.player = player
// playerController.allowsPictureInPicturePlayback = true
// playerController.delegate = self
// playerController.player?.play()
// present(playerController, animated: true, completion: nil)
// let player3 = AVPlayer(url: vidUrl!)
// let playerLayer3 = AVPlayerLayer(player: player3)
// playerLayer3.videoGravity = .resizeAspect
// playerLayer3.needsDisplayOnBoundsChange = true //
// playerLayer3.frame = self.videoImg.bounds // 1
//
// self.videoImg.layer.masksToBounds = true // 2
// self.videoImg.layer.addSublayer(playerLayer3)
// player3.play()
// let assetsLib = PHPhotoLibrary()
// assetsLib.
// assetsLib.writeVideoAtPath(toSavedPhotosAlbum: outputURL, completionBlock: nil)
print(outputURL, "outputURL.....")
if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) {
exportSession.outputURL = NSURL.fileURL(withPath: outputURL.path)
exportSession.outputFileType = AVFileType.mov
exportSession.shouldOptimizeForNetworkUse = true
// try to export the file and handle the status cases
exportSession.exportAsynchronously(completionHandler: {
switch exportSession.status {
case .failed:
if let _error = exportSession.error {
print("failedddd",_error)
failure(_error)
}
case .cancelled:
if let _error = exportSession.error {
print("cancellled")
failure(_error)
}
default:
print("finished")
success(outputURL)
}
})
} else {
print("failurrrrrrrr")
failure(nil)
}
}
}
Here the calling of Function
let vidUrl! = file:///private/var/mobile/Containers/Data/PluginKitPlugin/C6BD4529-DF11-4A67-B56E-C3A841D86D75/tmp/trim.CC7EABA8-08EC-4C33-8E6A-E85A5EBCFB64.MOV
let outputURL! = file:///var/mobile/Containers/Data/Application/9115DCFE-4134-48AD-84BF-452AC0D33278/Documents/y2matecom-ChallaOfficialFullVideoKhanSaabAYMediaRecordsLatestPunjabiSongs2016.mp3
mergeVideoWithAudio(videoUrl: vidUrl!, audioUrl: outputURL!) { resultUrl in
print("sucessssssssss",resultUrl)
} failure: { error in
print(error?.localizedDescription,"hgjjhfghdg")
}
Note:Every time the Failure Block calls and cause operation stopped
In my idea media type like mpeg4 is wrong. Where is the problem? What am i missing?

CGDisplayCreateImage not able to take screenshots(only returns wallpaper and task bar)

I want to take screenshot programmatically in Swift, macOS
I followed the below link
Programmatically Screenshot | Swift 3, macOS
Code is here
var displayCount: UInt32 = 0;
var result = CGGetActiveDisplayList(0, nil, &displayCount)
if (result != CGError.success) {
print("error: \(result)")
return
}
let allocated = Int(displayCount)
let activeDisplays = UnsafeMutablePointer<CGDirectDisplayID>.allocate(capacity: allocated)
result = CGGetActiveDisplayList(displayCount, activeDisplays, &displayCount)
if (result != CGError.success) {
print("error: \(result)")
return
}
for i in 1...displayCount {
let unixTimestamp = CreateTimeStamp()
let fileUrl = URL(fileURLWithPath: folderName + "\(unixTimestamp)" + "_" + "\(i)" + ".jpg", isDirectory: true)
let screenShot:CGImage = CGDisplayCreateImage(activeDisplays[Int(i-1)])!
let bitmapRep = NSBitmapImageRep(cgImage: screenShot)
let jpegData = bitmapRep.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [:])!
let fileManager = FileManager.default
do {
let documentDirectory = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = documentDirectory.appendingPathComponent("\(unixTimestamp)" + "_" + "\(i)" + ".jpg")
try jpegData.write(to: fileURL)
} catch {
print(error)
}
}
But I am getting this output.
enter image description here

Callback function of requestExportSession is never called. Issue only happens on iOS 13

I have a problem when trying to get a video file from the user's device.
This is the function that worked on iOS 12 which seems correct to me. The requestExportSession function is being called but never executes the callback code. I'm using Swift 4.
private func getVideo(asset:AssetViewModel, completion: #escaping (contentData?) -> ()) {
guard let avAsset = asset.video else {completion(nil); return}
guard self.createTempDirectory() == true else {completion(nil); return}
let options = PHVideoRequestOptions()
options.isNetworkAccessAllowed = true
let fileName = (avAsset as! AVURLAsset).url.lastPathComponent
let exportFileName = "\(asset.uuid).\(self.vidExt)"
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("upload").appendingPathComponent(exportFileName)
var fileSize: Int = 0
var success:Bool = false
let videoGroup = DispatchGroup()
videoGroup.enter()
PHImageManager.default().requestExportSession(forVideo: asset.image, options: options, exportPreset: AVAssetExportPreset1280x720) { (exportSession, info) in
guard let exportSession = exportSession else {success = false; videoGroup.leave(); return}
exportSession.outputURL = fileURL
exportSession.outputFileType = AVFileType.mp4
exportSession.exportAsynchronously(completionHandler: {
if exportSession.status == AVAssetExportSessionStatus.completed {
let asset = AVURLAsset(url: fileURL)
fileSize = asset.fileSize ?? 0
success = true
videoGroup.leave()
}
})
}
videoGroup.wait()
if success {
completion(contentData(imageURL: fileURL, fileSize: fileSize, uuid: asset.uuid, fileName: fileName, ext: self.vidExt))
} else {
completion(nil)
}
}

How I can edit or trim video start and point particular parts of video

How I can edit or trim video start and point particular parts of video?
Also I want to use slider to point out trimming start and end points.
func trimVideo(sourceURL: NSURL, destinationURL: NSURL, trimPoints: TrimPoints, completion: TrimCompletion?) {
assert(sourceURL.fileURL)
assert(destinationURL.fileURL)
let options = [ AVURLAssetPreferPreciseDurationAndTimingKey: true ]
let asset = AVURLAsset(URL: sourceURL, options: options)
let preferredPreset = AVAssetExportPresetPassthrough
if verifyPresetForAsset(preferredPreset, asset: asset) {
let composition = AVMutableComposition()
let videoCompTrack = composition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
let audioCompTrack = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
let assetVideoTrack: AVAssetTrack = asset.tracksWithMediaType(AVMediaTypeVideo).first as! AVAssetTrack
let assetAudioTrack: AVAssetTrack = asset.tracksWithMediaType(AVMediaTypeAudio).first as! AVAssetTrack
var compError: NSError?
var accumulatedTime = kCMTimeZero
for (startTimeForCurrentSlice, endTimeForCurrentSlice) in trimPoints {
let durationOfCurrentSlice = CMTimeSubtract(endTimeForCurrentSlice, startTimeForCurrentSlice)
let timeRangeForCurrentSlice = CMTimeRangeMake(startTimeForCurrentSlice, durationOfCurrentSlice)
videoCompTrack.insertTimeRange(timeRangeForCurrentSlice, ofTrack: assetVideoTrack, atTime: accumulatedTime, error: &compError)
audioCompTrack.insertTimeRange(timeRangeForCurrentSlice, ofTrack: assetAudioTrack, atTime: accumulatedTime, error: &compError)
if compError != nil {
NSLog("error during composition: \(compError)")
if let completion = completion {
completion(compError)
}
}
accumulatedTime = CMTimeAdd(accumulatedTime, durationOfCurrentSlice)
}
let exportSession = AVAssetExportSession(asset: composition, presetName: preferredPreset)
exportSession.outputURL = destinationURL
exportSession.outputFileType = AVFileTypeAppleM4V
exportSession.shouldOptimizeForNetworkUse = true
removeFileAtURLIfExists(destinationURL)
exportSession.exportAsynchronouslyWithCompletionHandler({ () -> Void in
if let completion = completion {
completion(exportSession.error)
}
})
} else {
NSLog("Could not find a suitable export preset for the input video")
let error = NSError(domain: "org.linuxguy.VideoLab", code: -1, userInfo: nil)
if let completion = completion {
completion(error)
}
}
}

Video Trimming failed with block AVAssetExportSessionStatus.Failed

I have converted this code to Swift language but i am getting this
Error: Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo=0x174278600 {NSUnderlyingError=0x170241d10 "The operation couldn’t be completed. (OSStatus error -12780.)", NSLocalizedFailureReason=An unknown error occurred (-12780), NSLocalizedDescription=The operation could not be completed} in (case AVAssetExportSessionStatus.Failed).
Kindly help me to resolved this
func cropVideo(sourceURL: NSURL)
{
let asset = AVURLAsset(URL: sourceURL, options: nil)
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality)
var error : NSError?
let file = "Finaloutput.mp4"
/* let paths : AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0]
let outputURL1 = paths[0] as? String*/
let nsDocumentDirectory = NSSearchPathDirectory.DocumentDirectory
let nsUserDomainMask = NSSearchPathDomainMask.UserDomainMask
let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
let outputURL1 = paths[0] as? String
let filemgr = NSFileManager.defaultManager()
filemgr.createDirectoryAtPath(outputURL1!, withIntermediateDirectories: true, attributes: nil, error: &error)
var outputURL = outputURL1!.stringByAppendingPathComponent(file)
filemgr.removeItemAtPath(outputURL, error: &error)
let FinalUrlTosave = NSURL(string: outputURL)
exportSession.outputURL=FinalUrlTosave
exportSession.shouldOptimizeForNetworkUse = true
// exportSession.outputFileType = AVFileTypeQuickTimeMovie
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
let start:CMTime
let duration:CMTime
start = CMTimeMakeWithSeconds(1.0, 600)
duration = CMTimeMakeWithSeconds(19.0, 600)
// let timeRangeForCurrentSlice = CMTimeRangeMake(start, duration)
let range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range
let destinationURL1 = NSURL(string: outputURL)
exportSession.exportAsynchronouslyWithCompletionHandler({
switch exportSession.status{
case AVAssetExportSessionStatus.Failed:
println("failed \(exportSession.error)")
case AVAssetExportSessionStatus.Cancelled:
println("cancelled \(exportSession.error)")
default:
println("complete....complete")
self.SaveVideoToPhotoLibrary(destinationURL1!)
}
})
}
func SaveVideoToPhotoLibrary(outputFileURL: NSURL)
{
assetsLibrary = ALAssetsLibrary()
let videoURL = outputFileURL as NSURL?
if let library = assetsLibrary{
if let url = videoURL{
library.writeVideoAtPathToSavedPhotosAlbum(url,
completionBlock: {(url: NSURL!, error: NSError!) in
print(url)
if let theError = error{
print("Error happened while saving the video")
print("The error is = \(theError)")
} else {
print("no errors happened")
}
})
} else {
print("Could not find the video in the app bundle")
}
}
}
Found Solution :
I have change this line and it works for me
let FinalUrlTosave = NSURL(fileURLWithPath: outputURL)
instead of
let FinalUrlTosave = NSURL(string: outputURL)
I was not getting exact path.