Подтвердить что ты не робот

Фиксирование ориентации при сшивании (слиянии) видео с использованием AVMutableComposition

TL;DR - ИЗМЕНИТЬ EDIT

Я создаю тестовое приложение в Swift, где я хочу сшить несколько видео вместе из каталога документов приложений, используя AVMutableComposition.

У меня был успех в этом, в какой-то степени, все мои видео сшиты вместе, и все показывает правильный размер и пейзаж.

Моя проблема заключается в том, что все видео отображаются в ориентации последнего видео в компиляции.

Я знаю, что для исправления этого мне нужно будет добавить инструкции слоя для каждого добавляемого трека, однако я не могу понять, что это правильно, и ответы, которые я нашел, вся компиляция, похоже, выходит в портретной ориентации с пейзажным видео просто масштабируется, чтобы вписаться в портретный вид, поэтому, когда я поворачиваю свой телефон на боку, чтобы просмотреть пейзажные видеоролики, они все еще малы, поскольку они масштабируются до размера портрета.

Это не тот результат, который я ищу, я хочу ожидаемую функциональность, т.е. если видеоролик является ландшафтом, он показывает масштабирование в портретном режиме, но если телефон повернут, я хочу, чтобы ландшафтное видео заполняло экран (как это было бы при простом просмотре пейзажного видео на фотографиях) и то же самое для портрета, так что при просмотре в портрете он отображается в полноэкранном режиме и, когда он поворачивается в сторону, видео масштабируется до размера ландшафта (например, при просмотре портретного видео на фотографиях).

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

Со всеми ответами я обнаружил, что это не так, и все они, казалось, имели очень неожиданное поведение при импорте видео с фотографий, чтобы добавить к компиляции, и такое же случайное поведение при добавлении видео, снятых с лицевой стороны камера (ясно, что мои текущие видеоролики, импортированные из библиотеки, и видеоролики "selfie" появляются с правильным размером без этих проблем).

Я ищу способ повернуть/масштабировать эти видео, чтобы они всегда показывались в правильной ориентации и масштабах, в зависимости от того, какой путь вокруг пользователя держит их телефон.

EDIT: теперь я знаю, что у меня не может быть как пейзажной, так и портретной ориентации в одном видео, поэтому ожидаемый результат, который я ищу, будет состоять в том, чтобы иметь окончательное видео в пейзаже ориентации. я выяснил, как сделать все ориентации и масштабы, чтобы получить все одинаково, но мой вывод - портретное видео, если кто-то может помочь мне изменить это, поэтому мой результат - это пейзаж, который будет оценен.

Ниже приведена моя функция для получения инструкции для каждого видео:

func videoTransformForTrack(asset: AVAsset) -> CGAffineTransform
{
    var return_value:CGAffineTransform?

    let assetTrack = asset.tracksWithMediaType(AVMediaTypeVideo)[0]

    let transform = assetTrack.preferredTransform
    let assetInfo = orientationFromTransform(transform)

    var scaleToFitRatio = UIScreen.mainScreen().bounds.width / assetTrack.naturalSize.width
    if assetInfo.isPortrait
    {
        scaleToFitRatio = UIScreen.mainScreen().bounds.width / assetTrack.naturalSize.height
        let scaleFactor = CGAffineTransformMakeScale(scaleToFitRatio, scaleToFitRatio)
        return_value = CGAffineTransformConcat(assetTrack.preferredTransform, scaleFactor)
    }
    else
    {
        let scaleFactor = CGAffineTransformMakeScale(scaleToFitRatio, scaleToFitRatio)
        var concat = CGAffineTransformConcat(CGAffineTransformConcat(assetTrack.preferredTransform, scaleFactor), CGAffineTransformMakeTranslation(0, UIScreen.mainScreen().bounds.width / 2))
        if assetInfo.orientation == .Down
        {
            let fixUpsideDown = CGAffineTransformMakeRotation(CGFloat(M_PI))
            let windowBounds = UIScreen.mainScreen().bounds
            let yFix = assetTrack.naturalSize.height + windowBounds.height
            let centerFix = CGAffineTransformMakeTranslation(assetTrack.naturalSize.width, yFix)
            concat = CGAffineTransformConcat(CGAffineTransformConcat(fixUpsideDown, centerFix), scaleFactor)
        }
        return_value = concat
    }
    return return_value!
}

И экспортер:

    // Create AVMutableComposition to contain all AVMutableComposition tracks
    let mix_composition = AVMutableComposition()
    var total_time = kCMTimeZero

    // Loop over videos and create tracks, keep incrementing total duration
    let video_track = mix_composition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())

    var instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: video_track)
    for video in videos
    {
        let shortened_duration = CMTimeSubtract(video.duration, CMTimeMake(1,10));
        let videoAssetTrack = video.tracksWithMediaType(AVMediaTypeVideo)[0]

        do
        {
            try video_track.insertTimeRange(CMTimeRangeMake(kCMTimeZero, shortened_duration),
                ofTrack: videoAssetTrack ,
                atTime: total_time)

            video_track.preferredTransform = videoAssetTrack.preferredTransform

        }
        catch _
        {
        }

        instruction.setTransform(videoTransformForTrack(video), atTime: total_time)

        // Add video duration to total time
        total_time = CMTimeAdd(total_time, shortened_duration)
    }

    // Create main instrcution for video composition
    let main_instruction = AVMutableVideoCompositionInstruction()
    main_instruction.timeRange = CMTimeRangeMake(kCMTimeZero, total_time)
    main_instruction.layerInstructions = [instruction]
    main_composition.instructions = [main_instruction]
    main_composition.frameDuration = CMTimeMake(1, 30)
    main_composition.renderSize = CGSize(width: UIScreen.mainScreen().bounds.width, height: UIScreen.mainScreen().bounds.height)

    let exporter = AVAssetExportSession(asset: mix_composition, presetName: AVAssetExportPreset640x480)
    exporter!.outputURL = final_url
    exporter!.outputFileType = AVFileTypeMPEG4
    exporter!.shouldOptimizeForNetworkUse = true
    exporter!.videoComposition = main_composition

    // 6 - Perform the Export
    exporter!.exportAsynchronouslyWithCompletionHandler()
    {
        // Assign return values based on success of export
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
                self.exportDidFinish(exporter!)
        })
    }

Извините за длинное объяснение, я просто хотел удостовериться, что был очень ясен с тем, что я просил, потому что другие ответы не сработали для меня.

4b9b3361

Ответ 1

Не уверен, что ваш orientationFromTransform() даст вам правильную ориентацию.

Я думаю, вы пытаетесь изменить его или попробовать что-нибудь вроде:

extension AVAsset {

    func videoOrientation() -> (orientation: UIInterfaceOrientation, device: AVCaptureDevicePosition) {
        var orientation: UIInterfaceOrientation = .Unknown
        var device: AVCaptureDevicePosition = .Unspecified

        let tracks :[AVAssetTrack] = self.tracksWithMediaType(AVMediaTypeVideo)
        if let videoTrack = tracks.first {

            let t = videoTrack.preferredTransform

            if (t.a == 0 && t.b == 1.0 && t.d == 0) {
                orientation = .Portrait

                if t.c == 1.0 {
                    device = .Front
                } else if t.c == -1.0 {
                    device = .Back
                }
            }
            else if (t.a == 0 && t.b == -1.0 && t.d == 0) {
                orientation = .PortraitUpsideDown

                if t.c == -1.0 {
                    device = .Front
                } else if t.c == 1.0 {
                    device = .Back
                }
            }
            else if (t.a == 1.0 && t.b == 0 && t.c == 0) {
                orientation = .LandscapeRight

                if t.d == -1.0 {
                    device = .Front
                } else if t.d == 1.0 {
                    device = .Back
                }
            }
            else if (t.a == -1.0 && t.b == 0 && t.c == 0) {
                orientation = .LandscapeLeft

                if t.d == 1.0 {
                    device = .Front
                } else if t.d == -1.0 {
                    device = .Back
                }
            }
        }

        return (orientation, device)
    }
}