Как перевернуть видео с помощью AVFoundation

Я записал видео с фронтальной камеры, и выход зеркальный...

Я пытался использовать AVMutablecomposition и layerinstructions, чтобы перевернуть видео, но безуспешно.

Гугление и поиск в Stack Overflow оказались бесплодными, поэтому держу пари, что простой и прямой пример того, как это сделать, принесет пользу многим.


person PinkFloydRocks    schedule 10.12.2015    source источник
comment
Если вы используете AVCaptureConnection для записи, я бы исправил проблему, установив ориентацию видео с помощью setVideoOrientation   -  person Lefteris    schedule 29.03.2016


Ответы (4)


Нет указаний на то, что вы используете для записи видео, я предположу, что AVCaptureSession + AVCaptureVideoDataOutput

lazy var videoFileOutput: AVCaptureVideoDataOutput = AVCaptureVideoDataOutput()
let v = videoFileOutput.connectionWithMediaType(AVMediaTypeVideo)
v.videoOrientation = .Portrait
v.videoMirrored = true
person Sean Lintern    schedule 24.03.2016
comment
Не могли бы вы предоставить больше контекста о том, как это можно использовать? Я использовал let videoRecorded = outputURL! как URL-адрес, который здесь не подходит - person ; 05.01.2019

Вы можете использовать -[AVMutableVideoCompositionLayerInstruction setTransform:atTime:]

CGAffineTransform transform = CGAffineTransformMakeTranslation(self.config.videoSize, 0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
[videoCompositionLayerInstruction setTransform:transform atTime:videoTime];

// then append video tracks
// [compositionTrack insertTimeRange:timeRange ofTrack:track atTime:atTime error:&error];

// apply instructions
videoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, composition.duration);
videoCompositionInstruction.layerInstructions = @[videoCompositionLayerInstruction];

videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.renderSize = CGSizeMake(self.config.videoSize, self.config.videoSize);
videoComposition.frameDuration = CMTimeMake(1, self.config.videoFrameRate);
videoComposition.instructions = @[videoCompositionInstruction];

https://github.com/ElfSundae/AVDemo/tree/ef2ca437d0d8dcb3ddEdit41c5a272c8753a6/AVSimpleOS

Состав экспорта:

AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:composition presetName:presetName];
exportSession.outputFileType = AVFileTypeMPEG4;
exportSession.outputURL = outputURL;
exportSession.shouldOptimizeForNetworkUse = YES;
// videoComposition contains transform instructions for video tracks
exportSession.videoComposition = videoComposition;
// audioMix contains background music for audio tracks
exportSession.audioMix = audioMix;

[exportSession exportAsynchronouslyWithCompletionHandler:^{
    AVAssetExportSessionStatus status = exportSession.status;
    if (status != AVAssetExportSessionStatusCompleted) {
        // exportSession.error
    } else {
        // exportSession.outputURL
    }
}];
person Elf Sundae    schedule 28.03.2016
comment
Спасибо за пример - а как бы вы экспортировали эту композицию в видео? - person PinkFloydRocks; 29.03.2016
comment
@PinkFloydRocks Я использую AVAssetExportSession, ответ обновлен. - person Elf Sundae; 30.03.2016
comment
CGAffineTransformMakeTranslation(self.config.videoSize, 0); . Ему нужно значение CGFloat, а не значение CGSize. Не знаю, как вы заставили этот код работать и разместить его здесь. - person ; 25.08.2017

Свифт 5. AVCaptureSession:

let movieFileOutput = AVCaptureMovieFileOutput()
let connection = movieFileOutput.connection(with: .video)

if connection?.isVideoMirroringSupported ?? false {
    connection?.isVideoMirrored = true
}

То же самое для PhotoOutput.

person atereshkov    schedule 20.08.2019

После того, как вы получите результат, преобразуйте свое видео

    func mirrorVideo(inputURL: URL, completion: @escaping (_ outputURL : URL?) -> ())
{
    let videoAsset: AVAsset = AVAsset( url: inputURL )
    let clipVideoTrack = videoAsset.tracks( withMediaType: AVMediaType.video ).first! as AVAssetTrack

    let composition = AVMutableComposition()
    composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: CMPersistentTrackID())

    let videoComposition = AVMutableVideoComposition()
    videoComposition.renderSize = CGSize(width: clipVideoTrack.naturalSize.height, height: clipVideoTrack.naturalSize.width)
    videoComposition.frameDuration = CMTimeMake(1, 30)

    let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: clipVideoTrack)

    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30))
    var transform:CGAffineTransform = CGAffineTransform(scaleX: -1.0, y: 1.0)
    transform = transform.translatedBy(x: -clipVideoTrack.naturalSize.width, y: 0.0)
    transform = transform.rotated(by: CGFloat(Double.pi/2))
    transform = transform.translatedBy(x: 0.0, y: -clipVideoTrack.naturalSize.width)

    transformer.setTransform(transform, at: kCMTimeZero)

    instruction.layerInstructions = [transformer]
    videoComposition.instructions = [instruction]

    // Export

    let exportSession = AVAssetExportSession(asset: videoAsset, presetName: AVAssetExportPreset640x480)!
    let fileName = UniqueIDGenerator.generate().appending(".mp4")
    let filePath = documentsURL.appendingPathComponent(fileName)
    let croppedOutputFileUrl = filePath
    exportSession.outputURL = croppedOutputFileUrl
    exportSession.outputFileType = AVFileType.mp4
    exportSession.videoComposition = videoComposition
    exportSession.exportAsynchronously {
        if exportSession.status == .completed {
            DispatchQueue.main.async(execute: {
                completion(croppedOutputFileUrl)
            })
            return
        } else if exportSession.status == .failed {
            print("Export failed - \(String(describing: exportSession.error))")
        }

        completion(nil)
        return
    }
}
person Yalcin Ozdemir    schedule 02.10.2017
comment
Это выглядит как стена кода без каких-либо объяснений. Было бы лучше, если бы вы немного детализировали его. - person atereshkov; 23.12.2019