2016-11-30 1 views
0

내 목표는 사용자가 사진에서 비디오를 선택하고 그 위에 레이블을 추가 할 수있게하는 것입니다.AVAssetExportSession 내보내기에 많은 시간이 필요합니다.

let audioAsset = AVURLAsset(url: selectedVideoURL) 
let videoAsset = AVURLAsset(url: selectedVideoURL) 
let mixComposition = AVMutableComposition() 
let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) 
let compositionAudioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) 
let clipVideoTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0] 
let clipAudioTrack = audioAsset.tracks(withMediaType: AVMediaTypeAudio)[0] 
do { 
    try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: clipVideoTrack, at: kCMTimeZero) 
    try compositionAudioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, audioAsset.duration), of: clipAudioTrack, at: kCMTimeZero) 
    compositionVideoTrack.preferredTransform = clipVideoTrack.preferredTransform 
} catch { 
    print(error) 
} 
var videoSize = clipVideoTrack.naturalSize 
if isVideoPortrait(asset: videoAsset) { 
    videoSize = CGSize(width: videoSize.height, height: videoSize.width) 
} 
let parentLayer = CALayer() 
let videoLayer = CALayer() 
parentLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height) 
videoLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height) 
parentLayer.addSublayer(videoLayer) 

// adding label 
let helloLabelLayer = CATextLayer() 
helloLabelLayer.string = "Hello" 
helloLabelLayer.font = "Signika-Semibold" as CFTypeRef? 
helloLabelLayer.fontSize = 30.0 
helloLabelLayer.contentsScale = mainScreen.scale 
helloLabelLayer.alignmentMode = kCAAlignmentNatural 
helloLabelLayer.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 50.0) 
parentLayer.addSublayer(helloLabelLayer) 

// creating composition 
let videoComp = AVMutableVideoComposition() 
videoComp.renderSize = videoSize 
videoComp.frameDuration = CMTimeMake(1, 30) 
videoComp.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer) 

let instruction = AVMutableVideoCompositionInstruction() 
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration) 
let layerInstruction = videoCompositionInstructionForTrack(track: compositionVideoTrack, asset: videoAsset) 
instruction.layerInstructions = [layerInstruction] 
videoComp.instructions = [instruction] 
if let assetExport = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPreset640x480) { 
    let filename = NSTemporaryDirectory().appending("video.mov") 

    if FileManager.default.fileExists(atPath: filename) { 
    do { 
     try FileManager.default.removeItem(atPath: filename) 
    } catch { 
     print(error) 
    } 
} 

let url = URL(fileURLWithPath: filename) 
assetExport.outputURL = url 
assetExport.outputFileType = AVFileTypeMPEG4 
assetExport.videoComposition = videoComp 
print(NSDate().timeIntervalSince1970) 
assetExport.exportAsynchronously { 
    print(NSDate().timeIntervalSince1970) 
    let library = ALAssetsLibrary() 
    library.writeVideoAtPath(toSavedPhotosAlbum: url, completionBlock: { 
     (url, error) in 
     switch assetExport.status { 
      case AVAssetExportSessionStatus.failed: 
       p("failed \(assetExport.error)") 
      case AVAssetExportSessionStatus.cancelled: 
       p("cancelled \(assetExport.error)") 
      default: 
       p("complete") 
       p(NSDate().timeIntervalSince1970) 
       if FileManager.default.fileExists(atPath: filename) { 
        do { 
         try FileManager.default.removeItem(atPath: filename) 
        } catch { 
         p(error) 
        } 
       } 
       print("Exported")          
     } 
    }) 
} 

구현 isVideoPortrait 기능 :

func isVideoPortrait(asset: AVAsset) -> Bool { 
    var isPortrait = false 
    let tracks = asset.tracks(withMediaType: AVMediaTypeVideo) 
    if tracks.count > 0 { 
     let videoTrack = tracks[0] 
     let t = videoTrack.preferredTransform 
     if t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0 { 
      isPortrait = true 
     } 
     if t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0 { 
      isPortrait = true 
     } 
     if t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0 { 
      isPortrait = false 
     } 
     if t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0 { 
      isPortrait = false 
     } 
    } 
    return isPortrait 
} 

그리고 video composition layer instruction의 마지막 기능은 다음과 같습니다

내가있어 무엇

func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction { 
    let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track) 
    let assetTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0] 
    let transform = assetTrack.preferredTransform 
    instruction.setTransform(transform, at: kCMTimeZero) 
    return instruction 
} 

코드는 잘 작동, 출력 비디오에는 레이블이 있지만 1 분 비디오를 선택하면 ta 28 초.

검색을 시도했지만 layerInsctuction 변형을 제거하려고했지만 효과가 없습니다.

추가 시도 : assetExport.shouldOptimizeForNetworkUse = false 아무 효과가 없습니다.

또한 AVAssetExportSessionAVAssetExportPresetPassthrough을 설정하려고했는데이 경우 1 초로 비디오를 내보내지만 라벨이 사라졌습니다.

내가 갇혀 있기 때문에 도움을 주시면 감사하겠습니다. 시간 내 줘서 고마워.

+0

시간이 많이 걸리는 작업 : 내보내기 및 사진 앨범으로 복사. 어느 것이 오래 걸리나요? – matt

+0

또한 : 시간이 오래 걸리고 그 이유를 알고 싶으면 Instruments를 사용하십시오. 너 그거 해봤 니? 뭘 말했지? – matt

+0

@matt 기본적으로 내보내기에는 많은 시간이 필요합니다. 사진 앨범에 복사하는 데 1 초가 걸립니다. 저는 악기를 사용하지 않았고 악기를 실행할 때 "Activity Monitor"를 사용해야합니까? – mikle94

답변

1

제가 생각할 수있는 유일한 방법은 비트 전송률과 해상도를 통해 품질을 낮추는 것입니다.

는 AssetExporter의 videoSettings에 적용되는 사전을 통해 이루어집니다,이에 대한 최적의 품질을 얻기 위해 나는 품질로 재생할 수있는 videoSettings을 변경하여 그때라는 프레임 워크 SDAVAssetExportSession

을 사용했다 작동하도록/속도.

let compression = [AVVideoAverageBitRateKey : 2097152(DESIRED_BITRATE),AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel] 

    let videoSettings = [AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : maxWidth, AVVideoHeightKey : maxHeight, AVVideoCompressionPropertiesKey:compression] 

이것은 내가 속도를 낼 수있는 유일한 방법이었습니다.

+0

감사합니다. 시간을 절반으로 줄이는 데 도움이되었습니다. – mikle94

+0

뭔가를 놓치지 않는 한 프레임 워크를 사용해야한다고 생각하지 않는다.'''AVAssetExportSession.exportPresets (compatibleWith : yourVideoAsset)'''에서 프리셋을 가져온 다음에 넘겨 준다. ''AVAssetExportSession'' 인스턴스화. – AdjunctProfessorFalcon

+0

이러한 특정 키가 AVAssetExportSession과 호환되지 않는다고 생각합니다. 아래쪽 AVAssetWriter를 사용해야합니다.이 AVAsetWriter는 파일에서이 드롭이 후드에서 수행하는 작업입니다. – SeanLintern88

1

이 질문에 직접 관련이없는,하지만 여기에 코드가 거꾸로 :

assetExport.exportAsynchronously { 
    let library = ALAssetsLibrary() 
    library.writeVideoAtPath(toSavedPhotosAlbum: url, completionBlock: { 
     switch assetExport.status { 

아니오 아니오 아니오. 먼저 자산 내보내기를 완료하십시오. 그런 다음 원하는 곳으로 다시 복사 할 수 있습니다. 그래서이 같이 갈 필요 :

assetExport.exportAsynchronously { 
    switch assetExport.status { 
    case .completed: 
     let library = ALAssetsLibrary() 
     library.writeVideoAtPath... 

기타 의견 :

  • ALAssetsLibrary가 죽었어요. 이것은 사용자의 사진 보관함에 복사하는 방법이 아닙니다. Photo 프레임 워크를 사용하십시오.

  • 테스트하지 않는 많은 경우가 있기 때문에 원래 코드는 매우 이상합니다. 당신은 단지 이라고 가정하고 default.completed을 의미합니다. 위험 해요.

+0

Ty 답장을 보내 주시면이 부분을 변경하겠습니다. – mikle94

+0

걱정할 필요가 없습니다. 미안하지만 이것은 진짜 대답이 아닙니다. 나는 그것을 삭제해야 할 것입니다. :) 그러나 코멘트에 모든 것을 말할 수있는 방법이 없었습니다. – matt

관련 문제