我正在尝试从UIImagePicker中选择任何视频的前15秒,然后将其保存到照片库。我正在使用AVFoundation抓紧前15秒,然后另存为新文件。我认为问题是我没有正确保存它,但不确定。此时,代码似乎可以正常运行。
import UIKit
import MobileCoreServices
import AVFoundation
import Photos
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var spliceSeconds = 15.0
var preferredPreset = AVAssetExportPresetPassthrough
@IBOutlet weak var bnLibrary: UIButton!
@IBOutlet weak var bnCamera: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
authVideoAccess()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func authVideoAccess() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
print("authorized")
break
case .denied:
//alert
print("denied")
break
case .notDetermined:
PHPhotoLibrary.requestAuthorization { (newStatus) in
print("status is \(newStatus)")
if newStatus == PHAuthorizationStatus.authorized {
/* do stuff here */
print("success")
}
}
print("It is not determined until now")
break
case .restricted:
print("permission restricted")
break
default:
// show something eles
print("default")
break
}
}
@IBAction func getVideo(_ sender: UIButton) {
if sender == bnCamera {
self.openCamera()
} else {
self.openVideoLibrary()
}
}
func openCamera() {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let myPickerController = UIImagePickerController()
myPickerController.delegate = self
myPickerController.sourceType = .camera
myPickerController.mediaTypes = [kUTTypeMovie as String]
self.present(myPickerController, animated: true, completion: nil)
}
}
func openVideoLibrary() {
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let myPickerController = UIImagePickerController()
myPickerController.delegate = self
myPickerController.sourceType = .photoLibrary
myPickerController.mediaTypes = [kUTTypeMovie as String, kUTTypeVideo as String]
self.present(myPickerController, animated: true, completion: nil)
}
}
//pragmark - delegate methods
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let vidUrl:URL = info[UIImagePickerControllerMediaURL]! as! URL
let options = [ AVURLAssetPreferPreciseDurationAndTimingKey: true ]
let asset = AVURLAsset(url: vidUrl, options: options)
if verifyPresetForAsset(preset: self.preferredPreset, asset: asset) {
let assetVideoTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaType.video).first!
let assetAudioTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaType.audio).first!
let startTime = assetAudioTrack.timeRange.start
let endTime = CMTimeMakeWithSeconds(spliceSeconds, startTime.timescale)
if assetVideoTrack.timeRange.duration.seconds > spliceSeconds {
trimVideo(assetVideoTrack: assetVideoTrack, assetAudioTrack: assetAudioTrack, startTime: startTime, endTime: endTime )
} else {
//show a message
}
}
//print(info[UIImagePickerControllerMediaURL]!)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 1
}
//pragmamark - trim stuff
func verifyPresetForAsset(preset: String, asset: AVAsset) -> Bool {
let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: asset)
let filteredPresets = compatiblePresets.filter { $0 == preset }
return filteredPresets.count > 0 || preset == AVAssetExportPresetPassthrough
}
func getNewFilePath() -> String {
let paths = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask)
return paths[0].absoluteString + "StorySplitter-" + UUID().uuidString + ".mp4"
}
func trimVideo(assetVideoTrack: AVAssetTrack, assetAudioTrack: AVAssetTrack, startTime: CMTime, endTime: CMTime) -> Void {
var accumulatedTime = kCMTimeZero
let durationOfCurrentSlice = CMTimeSubtract(endTime, startTime)
let timeRangeForCurrentSlice = CMTimeRangeMake(startTime, endTime)
let composition = AVMutableComposition()
let videoCompTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: CMPersistentTrackID())
let audioCompTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())
do {
try videoCompTrack?.insertTimeRange(timeRangeForCurrentSlice, of: assetVideoTrack, at: accumulatedTime)
try audioCompTrack?.insertTimeRange(timeRangeForCurrentSlice, of: assetAudioTrack, at: accumulatedTime)
accumulatedTime = CMTimeAdd(accumulatedTime, durationOfCurrentSlice)
}
catch let compError {
print("TrimVideo: error during composition: \(compError)")
}
accumulatedTime = CMTimeAdd(accumulatedTime, durationOfCurrentSlice)
let exportSession = AVAssetExportSession(asset: composition, presetName: self.preferredPreset)
exportSession?.outputURL = URL(fileURLWithPath: getNewFilePath())
exportSession?.outputFileType = AVFileType.mp4
exportSession?.shouldOptimizeForNetworkUse = true
exportSession?.exportAsynchronously {
print("finished saving")
}
}
}
答案 0 :(得分:0)
exportSession?.exportAsynchronously
不会保存到照片库,而只是保存到您指定的outputURL
,它位于应用程序的沙箱中。如果您打印出网址,则类似
file:///var/mobile/Containers/Data/Application/520D8354-B1D2-465F-93C2-54F2528D1AA9/Pictures/StorySplitter-528229F9-4D4F-4E28-82C9-3B1F2FDB215B.mp4
需要做一些额外的工作,以将上述mp4文件保存在图片Libraray中。
exportSession?.exportAsynchronously {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: newURL)
}) { saved, error in
// handle here
}
}
此外,您需要将func getNewFilePath() -> String
更改为FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask)
NSTemporaryDirectory().appendingFormat("yourVideoName.mov")
PHPPhotoLibrary似乎无法从FileManager访问URL,但可以通过NSTemporaryDirectory()进行访问
仅供参考:以上两个网址 fileManagerPath:
file:///var/mobile/Containers/Data/Application/520D8354-B1D2-465F-93C2-54F2528D1AA9/Pictures/StorySplitter-528229F9-4D4F-4E28-82C9-3B1F2FDB215B.mp4
tempPath:
/private/var/mobile/Containers/Data/Application/520D8354-B1D2-465F-93C2-54F2528D1AA9/tmp/video.mov
主要区别在于files:///
和private
,files:///
将使该URL成为绝对URL,因此在trimVideo
函数中,您必须使用{{1 }}作为导出URL。
由于相同的URL必须使用两次,并且URL(string: getNewFilePath())
每次都获得唯一的URL,因此您需要将其保存在变量中,以便在getNewFilePath())
中,您仍然可以引用相同的URL exportSession?.exportAsynchronously