Сбой без отчета - PullRequest
       26

Сбой без отчета

0 голосов
/ 05 сентября 2018

В моей программе есть класс, который объединяет множество видеофайлов для создания 1 общего видео. У меня есть 1 основной актив, который я использую в первую очередь, а остальные активы я использую сверху. Единственный используемый аудиофайл - из основного ресурса. Вот код:

import UIKit
import AVFoundation
import Photos


class Merger: NSObject {

    var controller:EditVideoViewController!
    var button:AddAssetButton!
    var view:UIView!
    var difference:Double!
    var changed:Bool = false
    var AI:AIView!

    convenience init(controller:EditVideoViewController, button:AddAssetButton) {
        self.init()
        self.controller = controller
        self.button = button

        self.view = UIView(frame: controller.view.bounds)
        self.view.backgroundColor = UIColor.black.withAlphaComponent(0.7)
        self.controller.view.addSubview(self.view)
    }  

    func setupAI() {
        self.AI = AIView(view: self.view)
        self.AI.start()
    }

    func removeAI() {
        self.AI.stop()
        self.AI.removeEverything()
    }

    //The video is displaying in Portrait after merge.
    func merge(completion:@escaping () -> Void, assets:[Asset]) {

        self.setupAI()

        let assets = assets.sorted(by: { $0.layer.zPosition < $1.layer.zPosition })
        if let firstAsset = controller.firstAsset {

            let mixComposition = AVMutableComposition()

            let firstTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo,
                                                                     preferredTrackID: Int32(kCMPersistentTrackID_Invalid))

            do {
                try firstTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, self.controller.realDuration),
                                           of: firstAsset.tracks(withMediaType: AVMediaTypeVideo)[0],
                                           at: kCMTimeZero)
            } catch _ {
                print("Failed to load first track")
            }

            var myTracks:[AVMutableCompositionTrack] = []

            for asset in assets {

                let secondTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo,
                                                                          preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
                secondTrack.preferredTransform = asset.asset.preferredTransform
                do {
                    try secondTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, asset.endTime-asset.beginTime),
                                               of: asset.asset.tracks(withMediaType: AVMediaTypeVideo)[0],
                                               at: CMTime(seconds: CMTimeGetSeconds(asset.beginTime), preferredTimescale: 600000))
                } catch _ {
                    print("Failed to load second track")
                }
                myTracks.append(secondTrack)
            }

            if let loadedAudioAsset = controller.audioAsset {
                let audioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: 0)
                do {
                    try audioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, self.controller.realDuration),
                                               of: loadedAudioAsset.tracks(withMediaType: AVMediaTypeAudio)[0] ,
                                               at: kCMTimeZero)
                } catch _ {
                    print("Failed to load Audio track")
                }
            }

            let mainInstruction = AVMutableVideoCompositionInstruction()
            mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, self.controller.realDuration)

            let firstInstruction = videoCompositionInstructionForTrack(firstTrack, firstAsset)
            var instructions:[AVMutableVideoCompositionLayerInstruction] = []
            var counter:Int = 0
            for tracks in myTracks {
                firstInstruction.setOpacity(0.0, at: assets[counter].beginTime)
                let secondInstruction = videoCompositionInstructionForTrack(tracks, assets[counter].asset, type:true)
                secondInstruction.setOpacity(0.0, at: assets[counter].endTime)
                firstInstruction.setOpacity(1.0, at: assets[counter].endTime)
                instructions.append(secondInstruction)
                counter += 1
            }

            mainInstruction.layerInstructions = [firstInstruction] + instructions
            let mainComposition = AVMutableVideoComposition()
            mainComposition.instructions = [mainInstruction]
            mainComposition.frameDuration = CMTimeMake(1, 30)
            mainComposition.renderSize = self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize

            let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
            let savePath = (documentDirectory as NSString).appendingPathComponent("mergeVideo.mov")
            let url = URL(fileURLWithPath: savePath)
           _ = try? FileManager().removeItem(at: url)

            guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
            exporter.outputFileType = AVFileTypeMPEG4
            exporter.outputURL = url
            exporter.videoComposition = mainComposition


            exporter.exportAsynchronously(completionHandler: {
                DispatchQueue.main.async(execute: {
                    self.exportDidFinish(exporter)
                    self.removeAI()
                    completion()
                })
            })
        }
    }
    func exportDidFinish(_ exporter:AVAssetExportSession) {
         if(exporter.status == AVAssetExportSessionStatus.completed) {
            print("cool")
        }
        else if(exporter.status == AVAssetExportSessionStatus.failed) {
            print(exporter.error as Any)
        }
    }

    func videoCompositionInstructionForTrack(_ track: AVCompositionTrack, _ asset: AVAsset, type:Bool = false) -> AVMutableVideoCompositionLayerInstruction {
        let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
        let assetTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0]

        var transform = assetTrack.preferredTransform
        let assetInfo = orientationFromTransform(transform)
        let width = self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.width/assetTrack.naturalSize.width
        var height = self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.height

        if assetInfo.isPortrait {
            //Vert Video taken from camera -- vert video from lib
            height = self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.width
            transform = transform.scaledBy(x: height, y: height)
            let movement = ((1/height)*assetTrack.naturalSize.height)-assetTrack.naturalSize.height
            transform = transform.translatedBy(x: 0, y: movement)
            let totalBlackDistance = self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.width-transform.tx
            transform = transform.translatedBy(x: 0, y: -(totalBlackDistance/2)*(1/height))

        } else {
            //Main Video -- hor photo from camera -- hor video from camera -- hor photo frmo lib -- hor vid frmo lib -- vert photos lib - vert photos camera
            transform = transform.scaledBy(x: width, y: height)
            let scale:CGFloat = ((self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.height)*(assetTrack.naturalSize.width))/self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.width
            transform = transform.scaledBy(x: scale, y: 1)
            let movement = ((self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.width-((self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.height)*(assetTrack.naturalSize.width)))/2)*(1/(self.controller.firstAsset!.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.height))
            transform = transform.translatedBy(x: movement, y: 0)
        }
        instruction.setTransform(transform, at: kCMTimeZero)
        return instruction
    }

    func orientationFromTransform(_ transform: CGAffineTransform) -> (orientation: UIImageOrientation, isPortrait: Bool) {
        var assetOrientation = UIImageOrientation.up
        var isPortrait = false
        if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 {
            assetOrientation = .right
            isPortrait = true
        } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 {
            assetOrientation = .left
            isPortrait = true
        } else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 {
            assetOrientation = .up
        } else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 {
            assetOrientation = .down
        }

        return (assetOrientation, isPortrait)
    }
}

Для моего телефона iPhone 6s это никогда не падает, и у меня никогда не возникает проблем.

Один из моих тестеров, у которого есть iPhone 5s, случайно вылетает во время процесса экспорта. Когда его телефон выходит из строя, он, кажется, на самом деле не столько ломается, сколько зависает. Приложение полностью закрывается и ничего не происходит. В окно моего Организатора не отправляется отчет о сбоях (это часто случается), и, похоже, никаких проблем не возникает. Еще одна проблема с тестированием это его приложение делает это на разных. Он не падает каждый раз, даже с теми же активами. Если бы я мог регулярно дублировать эту проблему с некоторой стандартизацией, у меня не было бы так много проблем. Но не совсем уверен, куда идти отсюда.

Этот тестер не расположен рядом со мной - я использую окно органайзера Apple для получения отчетов о сбоях

Потенциальные решения:

Память: я думал, что это может быть проблема с памятью, так как я знаю, что если память перегружена, это приведет к закрытию приложения. Тем не менее, ничего не отправлено в «DidReceiveMemoryWarning», приложение просто завершается.

Есть предложения / решения? Вся помощь приветствуется.

1 Ответ

0 голосов
/ 06 сентября 2018

Xcode Organizer не предоставляет каждый отдельный отчет о сбое для вашего приложения, только Apple знает, как они выбирают, что они предоставляют, а какие - нет. Во время бета-тестирования с помощью TestFlight отчет может занять несколько дней, если пользователь одобрит предоставление данных отчета о сбоях разработчикам.

Даже если приложение будет убито ОС по другой причине, например чрезмерное потребление памяти, отчет о сбое будет записан на устройство и предоставлен Apple, если все требования для его предоставления положительны, например, пользователь подтвердил предоставление этих данных.

Без отчета невозможно сказать, что произошло и почему, поэтому предположение о проблеме использования памяти является диким предположением, а указанная причина недопустима.

Вам необходимо получить отчет о сбое и обозначить его символом. Ваш пользователь может найти отчет о сбое на устройстве, перейдя в «Настройки> общие> о> Диагностика и данные об использовании», а затем скопировать содержимое и вставить его, например, в электронное письмо и отправить его вам.

...