【问题标题】:how to trim a video in swift for a particular time如何在特定时间快速修剪视频
【发布时间】:2016-06-12 07:02:47
【问题描述】:

我正在处理一项任务,我必须将录制的视频从用户输入或选择的特定起点修剪到特定终点。 我该怎么做。正如我之前使用的UIVideoEditorController,但我不想使用默认视图,我想直接修剪视频。

let FinalUrlTosave = NSURL(string: "\(newURL)")
    exportSession!.outputURL=FinalUrlTosave
    exportSession!.shouldOptimizeForNetworkUse = true
    // exportSession.outputFileType = AVFileTypeQuickTimeMovie
    exportSession!.outputFileType = AVFileTypeQuickTimeMovie;
    let start:CMTime
    let duration:CMTime
    var st = starttime.doubleValue
    var ed = endTime.doubleValue
    start = CMTimeMakeWithSeconds(st, 600)
    duration = CMTimeMakeWithSeconds(ed, 600)
    // let timeRangeForCurrentSlice = CMTimeRangeMake(start, duration)
    let range = CMTimeRangeMake(start, duration);
    exportSession!.timeRange = range

       exportSession!.exportAsynchronouslyWithCompletionHandler({
        switch exportSession!.status{
        case  AVAssetExportSessionStatus.Failed:

            print("failed \(exportSession!.error)")
        case AVAssetExportSessionStatus.Cancelled:
            print("cancelled \(exportSession!.error)")
        default:
            print("complete....complete")
            //                self.SaveVideoToPhotoLibrary(destinationURL1!)

        }
    })

我正在尝试使用它来实现我的目标,但没有成功。

错误信息:

失败的可选(错误域=NSURLErrorDomain代码=-1100“ 在此服务器上找不到请求的 URL。” UserInfo={NSErrorFailingURLStringKey=file:///var/mobile/Containers/Data/Application/E68D3BFD-6923-4EA6-9FB3-C020CE4AA9D4/Documents/moment/jGq_9AUFa47s2ZiiPP4x.mp4, NSErrorFailingURLKey=file:///var/mobile/Containers/Data/Application/E68D3BFD-6923-4EA6-9FB3-C020CE4AA9D4/Documents/moment/jGq_9AUFa47s2ZiiPP4x.mp4, NSLocalizedDescription=在此未找到请求的 URL 服务器,NSUnderlyingError=0x1553c220 {错误域=N

第二次出现错误:

失败的可选(错误域=NSURLErrorDomain 代码=-3000“不能 创建文件” UserInfo={NSUnderlyingError=0x14e00000 {错误 域=NSOSStatusErrorDomain 代码=-12124 "(null)"}, NSLocalizedDescription=无法创建文件})

【问题讨论】:

  • 它是怎么失败的?什么错误?
  • 我将一个 url 传递给 newURL 参数,它总是进入失败的错误块...
  • 是的,但是错误信息是什么
  • 嗯,您的问题的根源不是在错误消息中明确说明了吗? :) “在此服务器上找不到请求的 URL。” 文件路径错误。
  • 我发现使用NSURL(string: "...") 可能会与某些api 发生意外行为。如果是本地文件,请尝试使用NSURL(fileURLWithPath: "...")

标签: ios swift trim avassetexportsession uivideoeditorcontroller


【解决方案1】:

我使用这种方法找到了我的解决方案,它就像一个魅力......

func cropVideo(sourceURL1: NSURL, statTime:Float, endTime:Float)
{
    let manager = NSFileManager.defaultManager()
    
    guard let documentDirectory = try? manager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true) else {return}
    guard let mediaType = "mp4" as? String else {return}
    guard let url = sourceURL1 as? NSURL else {return}
    
    if mediaType == kUTTypeMovie as String || mediaType == "mp4" as String {
        let asset = AVAsset(URL: url)
        let length = Float(asset.duration.value) / Float(asset.duration.timescale)
        print("video length: \(length) seconds")
        
        let start = statTime
        let end = endTime
        
        var outputURL = documentDirectory.URLByAppendingPathComponent("output")
        do {
            try manager.createDirectoryAtURL(outputURL, withIntermediateDirectories: true, attributes: nil)
            let name = Moment.newName()
            outputURL = outputURL.URLByAppendingPathComponent("\(name).mp4")
        }catch let error {
            print(error)
        }
        
        //Remove existing file
        _ = try? manager.removeItemAtURL(outputURL)
        
        
        guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else {return}
        exportSession.outputURL = outputURL
        exportSession.outputFileType = AVFileTypeMPEG4
        
        let startTime = CMTime(seconds: Double(start ?? 0), preferredTimescale: 1000)
        let endTime = CMTime(seconds: Double(end ?? length), preferredTimescale: 1000)
        let timeRange = CMTimeRange(start: startTime, end: endTime)
        
        exportSession.timeRange = timeRange
        exportSession.exportAsynchronouslyWithCompletionHandler{
            switch exportSession.status {
            case .Completed:
                print("exported at \(outputURL)")
               self.saveVideoTimeline(outputURL)
            case .Failed:
                print("failed \(exportSession.error)")
                
            case .Cancelled:
                print("cancelled \(exportSession.error)")
                
            default: break
            }
        }
    }
}

斯威夫特 5

    func cropVideo(sourceURL1: URL, statTime:Float, endTime:Float)
{
    let manager = FileManager.default

    guard let documentDirectory = try? manager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else {return}
    let mediaType = "mp4"
    if mediaType == kUTTypeMovie as String || mediaType == "mp4" as String {
        let asset = AVAsset(url: sourceURL1 as URL)
        let length = Float(asset.duration.value) / Float(asset.duration.timescale)
        print("video length: \(length) seconds")

        let start = statTime
        let end = endTime

        var outputURL = documentDirectory.appendingPathComponent("output")
        do {
            try manager.createDirectory(at: outputURL, withIntermediateDirectories: true, attributes: nil)
            outputURL = outputURL.appendingPathComponent("\(UUID().uuidString).\(mediaType)")
        }catch let error {
            print(error)
        }

        //Remove existing file
        _ = try? manager.removeItem(at: outputURL)


        guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else {return}
        exportSession.outputURL = outputURL
        exportSession.outputFileType = .mp4

        let startTime = CMTime(seconds: Double(start ), preferredTimescale: 1000)
        let endTime = CMTime(seconds: Double(end ), preferredTimescale: 1000)
        let timeRange = CMTimeRange(start: startTime, end: endTime)

        exportSession.timeRange = timeRange
        exportSession.exportAsynchronously{
            switch exportSession.status {
            case .completed:
                print("exported at \(outputURL)")
            case .failed:
                print("failed \(exportSession.error)")

            case .cancelled:
                print("cancelled \(exportSession.error)")

            default: break
            }
        }
    }
}

【讨论】:

  • 很好的答案! @Parv Bhasker
  • 感谢它的工作,但输出视频没有音频。
【解决方案2】:

为此提供 swift4 版本。

static func cropVideo(sourceURL: URL, startTime: Double, endTime: Double, completion: ((_ outputUrl: URL) -> Void)? = nil)
{
    let fileManager = FileManager.default
    let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]

    let asset = AVAsset(url: sourceURL)
    let length = Float(asset.duration.value) / Float(asset.duration.timescale)
    print("video length: \(length) seconds")

    var outputURL = documentDirectory.appendingPathComponent("output")
    do {
        try fileManager.createDirectory(at: outputURL, withIntermediateDirectories: true, attributes: nil)
        outputURL = outputURL.appendingPathComponent("\(sourceURL.lastPathComponent).mp4")
    }catch let error {
        print(error)
    }

    //Remove existing file
    try? fileManager.removeItem(at: outputURL)

    guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else { return }
    exportSession.outputURL = outputURL
    exportSession.outputFileType = .mp4

    let timeRange = CMTimeRange(start: CMTime(seconds: startTime, preferredTimescale: 1000),
                                end: CMTime(seconds: endTime, preferredTimescale: 1000))

    exportSession.timeRange = timeRange
    exportSession.exportAsynchronously {
        switch exportSession.status {
        case .completed:
            print("exported at \(outputURL)")
            completion?(outputURL)
        case .failed:
            print("failed \(exportSession.error.debugDescription)")
        case .cancelled:
            print("cancelled \(exportSession.error.debugDescription)")
        default: break
        }
    }
}

【讨论】:

    【解决方案3】:

    这个可以完成工作并解决旋转问题。

    extension AVAsset {
        func assetByTrimming(startTime: CMTime, endTime: CMTime) throws -> AVAsset {
            let duration = CMTimeSubtract(endTime, startTime)
            let timeRange = CMTimeRange(start: startTime, duration: duration)
    
            let composition = AVMutableComposition()
    
            do {
                for track in tracks {
                    let compositionTrack = composition.addMutableTrack(withMediaType: track.mediaType, preferredTrackID: track.trackID)
                    compositionTrack?.preferredTransform = track.preferredTransform
                    try compositionTrack?.insertTimeRange(timeRange, of: track, at: CMTime.zero)
                }
            } catch let error {
                throw TrimError("error during composition", underlyingError: error)
            }
    
            return composition
        }
    
    struct TrimError: Error {
        let description: String
        let underlyingError: Error?
    
        init(_ description: String, underlyingError: Error? = nil) {
            self.description = "TrimVideo: " + description
            self.underlyingError = underlyingError
        }
    }
    

    【讨论】:

      【解决方案4】:
      func cropVideo1(_ sourceURL1: URL, statTime:Float, endTime:Float){
              let videoAsset: AVAsset = AVAsset(url: sourceURL1) as AVAsset
              let composition = AVMutableComposition()
              composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
      
              let videoComposition = AVMutableVideoComposition()
              videoComposition.renderSize = CGSize(width: 1280, height: 768)
              videoComposition.frameDuration = CMTimeMake(8, 15)
      
              let instruction = AVMutableVideoCompositionInstruction()
              let length = Float(videoAsset.duration.value)
              print(length)
      
              instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30))
      
              let start = statTime
              let end = endTime
      
              let exportSession = AVAssetExportSession(asset: videoAsset, presetName: AVAssetExportPresetHighestQuality)!
              exportSession.outputFileType = AVFileTypeMPEG4
      
              let startTime = CMTime(seconds: Double(start ), preferredTimescale: 1000)
              let endTime = CMTime(seconds: Double(end ), preferredTimescale: 1000)
              let timeRange = CMTimeRange(start: startTime, end: endTime)
      
              exportSession.timeRange = timeRange
      
              let formatter = DateFormatter()
              formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
      
              let date = Date()
      
              let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
              let outputPath = "\(documentsPath)/\(formatter.string(from: date)).mp4"
              let outputURL = URL(fileURLWithPath: outputPath)
      
              exportSession.outputURL = outputURL
              exportSession.outputFileType = AVFileTypeQuickTimeMovie
              print("sucess")
              exportSession.exportAsynchronously(completionHandler: { () -> Void in
                  DispatchQueue.main.async(execute: {
                      self.exportDidFinish(exportSession)
                      print("sucess")
      
      
                  })
              })
          }
          func exportDidFinish(_ session: AVAssetExportSession) {
      
              if session.status == AVAssetExportSessionStatus.completed {
                  let outputURL = session.outputURL
                  let library = ALAssetsLibrary()
                  if library.videoAtPathIs(compatibleWithSavedPhotosAlbum: outputURL) {
                      library.writeVideoAtPath(toSavedPhotosAlbum: outputURL) { alAssetURL, error in
                          if error != nil {
                              DispatchQueue.main.async(execute: {
                                  print("Failed to save video")
                              })
                          } else {
                              DispatchQueue.main.async(execute: {
                                  Print("Sucessfully saved Video")
                              })
                          }
                          self.activityIndicator.stopAnimating()
                      }
                  }
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-10-04
        • 1970-01-01
        • 1970-01-01
        • 2019-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多