Видео повернуто после применения AVVideoComposition

После применения AVVideoComposition к моему AVPlayerItem фильтр, который я применяю, работает, но видео поворачивается в AVPlayerLayer.

Я точно знаю, что проблема не в отфильтрованном кадре, потому что, если я покажу кадр в формате UIImageView, кадр будет отображаться на 100% правильно.

Видео отображается правильно, пока я не применяю videoComposition. Установка videoGravity на AVPlayerLayer не помогает.

Видео поворачивается на 90º по часовой стрелке и растягивается в слое.

По сути, видео отлично отображается в AVPlayerLayer до того, как AVPlayerItem подается через AVMutableVideoComposition. Как только это происходит, видео поворачивается на -90º, а затем масштабируется, чтобы соответствовать тем же размерам, что и видео перед фильтрацией. Это говорит мне о том, что он не понимает, что его преобразование уже правильное, и поэтому повторно применяет преобразование к себе.

Почему это происходит, и как я могу это исправить?

Вот код:

private func filterVideo(with filter: Filter?) {
    if let player = player, let playerItem = player.currentItem {
        let composition = AVMutableComposition()
        let videoAssetTrack = playerItem.asset.tracks(withMediaType: .video).first
        let videoCompositionTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
        try? videoCompositionTrack?.insertTimeRange(CMTimeRange(start: kCMTimeZero, duration: playerItem.asset.duration), of: videoAssetTrack!, at: kCMTimeZero)
        videoCompositionTrack?.preferredTransform = videoAssetTrack!.preferredTransform

        let videoComposition = AVMutableVideoComposition(asset: composition, applyingCIFiltersWithHandler: { (request) in
            let filteredImage = <...>
            request.finish(with: filteredImage, context: nil)
        })

        playerItem.videoComposition = videoComposition
    }
}

person IHaveAQuestion    schedule 04.07.2017    source источник
comment
является ли ваша видеокомпозиция пользовательской или вы используете `AVVideoComposition (_:, применяя CIFiltersWithHandler:)?   -  person Tiko    schedule 04.07.2017
comment
@Tiko, я использую applyingCIFiltersWithHandler   -  person IHaveAQuestion    schedule 04.07.2017
comment
Попробуйте удалить строку: videoCompositionTrack?.preferredTransform = videoAssetTrack!.preferredTransform   -  person ninjaproger    schedule 05.07.2017
comment
@ninjaproger, из-за этого видео исчезает, хотя для некоторых видео оно странным образом работает только время от времени. Иногда это ставит видео на паузу и не получает фильтр.   -  person IHaveAQuestion    schedule 05.07.2017
comment
@IHaveAQuestion это происходит только с вертикальным или горизонтальным видео или с обоими? Проверьте это и дайте мне знать.   -  person impression7vx    schedule 07.07.2017
comment
@ impression7vx на самом деле это происходит только с одним видео. Любая причина, по которой это может произойти? Это мой вопрос, если вы можете помочь.   -  person IHaveAQuestion    schedule 07.07.2017
comment
Привет! Вы решили это? Я и моя команда застряли в одной и той же проблеме...   -  person Roi Mulia    schedule 10.06.2018
comment
@RoiMulia, посмотри мой ответ ниже   -  person IHaveAQuestion    schedule 11.06.2018


Ответы (5)


У вас проблема с renderingSize AVVideoComposition. Вы должны применить преобразование к AVMutableVideoCompositionInstruction (т.е. Rotate и translate преобразование).

Я сделал это в Objective-c и публикую свой код. Вы можете преобразовать синтаксис в Swift.

Цель-c

//------------------------------------
//      FIXING ORIENTATION
//------------------------------------


AVMutableVideoCompositionInstruction * MainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
MainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeAdd(firstAsset.duration, secondAsset.duration));

AVMutableVideoCompositionLayerInstruction *FirstlayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:secondTrack]; // second

AVAssetTrack *FirstAssetTrack = [[firstAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
UIImageOrientation FirstAssetOrientation_  = UIImageOrientationUp;
BOOL  isFirstAssetPortrait_  = NO;
CGAffineTransform firstTransform = FirstAssetTrack.preferredTransform;
if(firstTransform.a == 0 && firstTransform.b == 1.0 && firstTransform.c == -1.0 && firstTransform.d == 0)  {FirstAssetOrientation_= UIImageOrientationRight; isFirstAssetPortrait_ = YES;}
if(firstTransform.a == 0 && firstTransform.b == -1.0 && firstTransform.c == 1.0 && firstTransform.d == 0)  {FirstAssetOrientation_ =  UIImageOrientationLeft; isFirstAssetPortrait_ = YES;}
if(firstTransform.a == 1.0 && firstTransform.b == 0 && firstTransform.c == 0 && firstTransform.d == 1.0)   {FirstAssetOrientation_ =  UIImageOrientationUp;}
if(firstTransform.a == -1.0 && firstTransform.b == 0 && firstTransform.c == 0 && firstTransform.d == -1.0) {FirstAssetOrientation_ = UIImageOrientationDown;}
CGFloat FirstAssetScaleToFitRatio = 320.0/FirstAssetTrack.naturalSize.width;
if(isFirstAssetPortrait_){
    FirstAssetScaleToFitRatio = 320.0/FirstAssetTrack.naturalSize.height;
    CGAffineTransform FirstAssetScaleFactor = CGAffineTransformMakeScale(FirstAssetScaleToFitRatio,FirstAssetScaleToFitRatio);
    [FirstlayerInstruction setTransform:CGAffineTransformConcat(FirstAssetTrack.preferredTransform, FirstAssetScaleFactor) atTime:kCMTimeZero];
}else{
    CGAffineTransform FirstAssetScaleFactor = CGAffineTransformMakeScale(FirstAssetScaleToFitRatio,FirstAssetScaleToFitRatio);
    [FirstlayerInstruction setTransform:CGAffineTransformConcat(CGAffineTransformConcat(FirstAssetTrack.preferredTransform, FirstAssetScaleFactor),CGAffineTransformMakeTranslation(0, 160)) atTime:kCMTimeZero];
}
[FirstlayerInstruction setOpacity:0.0 atTime:firstAsset.duration];



AVMutableVideoCompositionLayerInstruction *SecondlayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:firstTrack];
AVAssetTrack *SecondAssetTrack = [[secondAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
UIImageOrientation SecondAssetOrientation_  = UIImageOrientationUp;
BOOL  isSecondAssetPortrait_  = NO;
CGAffineTransform secondTransform = SecondAssetTrack.preferredTransform;
if(secondTransform.a == 0 && secondTransform.b == 1.0 && secondTransform.c == -1.0 && secondTransform.d == 0)  {SecondAssetOrientation_= UIImageOrientationRight; isSecondAssetPortrait_ = YES;}
if(secondTransform.a == 0 && secondTransform.b == -1.0 && secondTransform.c == 1.0 && secondTransform.d == 0)  {SecondAssetOrientation_ =  UIImageOrientationLeft; isSecondAssetPortrait_ = YES;}
if(secondTransform.a == 1.0 && secondTransform.b == 0 && secondTransform.c == 0 && secondTransform.d == 1.0)   {SecondAssetOrientation_ =  UIImageOrientationUp;}
if(secondTransform.a == -1.0 && secondTransform.b == 0 && secondTransform.c == 0 && secondTransform.d == -1.0) {SecondAssetOrientation_ = UIImageOrientationDown;}
CGFloat SecondAssetScaleToFitRatio = 320.0/SecondAssetTrack.naturalSize.width;
if(isSecondAssetPortrait_){
    SecondAssetScaleToFitRatio = 320.0/SecondAssetTrack.naturalSize.height;
    CGAffineTransform SecondAssetScaleFactor = CGAffineTransformMakeScale(SecondAssetScaleToFitRatio,SecondAssetScaleToFitRatio);
    [SecondlayerInstruction setTransform:CGAffineTransformConcat(SecondAssetTrack.preferredTransform, SecondAssetScaleFactor) atTime:firstAsset.duration];
}else{
    ;
    CGAffineTransform SecondAssetScaleFactor = CGAffineTransformMakeScale(SecondAssetScaleToFitRatio,SecondAssetScaleToFitRatio);
    [SecondlayerInstruction setTransform:CGAffineTransformConcat(CGAffineTransformConcat(SecondAssetTrack.preferredTransform, SecondAssetScaleFactor),CGAffineTransformMakeTranslation(0, 160)) atTime:secondAsset.duration];
}


MainInstruction.layerInstructions = [NSArray arrayWithObjects:SecondlayerInstruction,nil];;

AVMutableVideoComposition *MainCompositionInst = [AVMutableVideoComposition videoComposition];
MainCompositionInst.instructions = [NSArray arrayWithObject:MainInstruction];
MainCompositionInst.frameDuration = CMTimeMake(1, 30);
MainCompositionInst.renderSize = CGSizeMake(320.0, 480.0);


// Now , you have Orientation Fixed Instrucation layer
// add this composition to your video ????
// If you want to export Video than you can do like below

NSString *documentsDirectory = [NSHomeDirectory()
stringByAppendingPathComponent:@"Documents"];
NSString *myPathDocs = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"final_merged_video-%d.mp4",arc4random() % 1000]];


NSURL *url = [NSURL fileURLWithPath:myPathDocs];
// 5 - Create exporter
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
presetName:AVAssetExportPreset640x480];
exporter.outputURL=url;
exporter.videoComposition=MainCompositionInst;
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^
{

[[AppDelegate Getdelegate] hideIndicator];

[self exportDidFinish:exporter];
});
}];

для Swift см. этот ответ Нажмите здесь

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

#define degreeToRadian(x) (M_PI * x / 180.0)

[_playerLayer setAffineTransform:CGAffineTransformMakeRotation(degreeToRad‌​ian(degree))]
person Dhiru    schedule 12.07.2017
comment
При установке videoInstruction в videoComposition обработчик композиции не вызывается. Я предполагаю, что мне не хватает чего-то очевидного. Любые идеи? Это сводит меня с ума! - person IHaveAQuestion; 12.07.2017
comment
Кроме того, я не пытаюсь объединять видео: я пытаюсь отфильтровать один ресурс. - person IHaveAQuestion; 13.07.2017
comment
Пожалуйста помогите пожалуйста!! Я был бы счастлив вручить тебе награду!!! - person IHaveAQuestion; 13.07.2017
comment
все видео растянуто или у некоторых ресурсов есть эта проблема ??, если все видео растянуто, вы можете повернуть свое видео на желаемый угол градуса [_playerLayer setAffineTransform:CGAffineTransformMakeRotation(degreeToRadian(degree))]; @IHaveAQuestion - person Dhiru; 14.07.2017
comment
где градус к радиану равен #define degreeToRadian(x) (M_PI * x / 180.0) - person Dhiru; 14.07.2017
comment
также см. это для получения дополнительной помощи, такой же, как мой ответ, но более объясненный -in-ios/" rel="nofollow noreferrer">abdulazeem.wordpress.com/2012/04/02/ - person Dhiru; 14.07.2017
comment
Dhiru, к сожалению, это не сработало, потому что когда я задаю инструкцию на видеокомпозицию, обработчик видеокомпозиции не вызывается, а значит, видео не фильтруется. - person IHaveAQuestion; 14.07.2017
comment
В дополнение к ^ установка преобразования слоя не решает проблему масштабирования. - person IHaveAQuestion; 14.07.2017
comment
В вашем коде, где сделать поворот? Можете ли вы привести пример? Спасибо. - person Somoy Das Gupta; 21.04.2019

Если вы пытаетесь воспроизвести AVMutableCompostion, вы должны установить preferredTransform AVAssetTrack на preferredTransform AVMutableCompositionTrack.

let asset = AVAsset(url: url!)

let composition = AVMutableComposition()
let compositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)

let videoTrack = asset.tracks(withMediaType: AVMediaTypeVideo).first

try? compositionTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, asset.duration), of: videoTrack!, at: kCMTimeZero)

compositionTrack.preferredTransform = (videoTrack?.preferredTransform)!

let playerItem = AVPlayerItem(asset: composition)
let filter = CIFilter(name: "CIColorInvert")
playerItem.videoComposition = AVVideoComposition(asset: composition, applyingCIFiltersWithHandler: { (request: AVAsynchronousCIImageFilteringRequest) in
            filter?.setValue(request.sourceImage, forKey: kCIInputImageKey)
            request.finish(with: (filter?.outputImage)!, context: nil)
        })
 .... the rest of code
person Tiko    schedule 04.07.2017
comment
К сожалению, это не сработало. Видео все еще мутное - person IHaveAQuestion; 05.07.2017
comment
Можете ли вы попытаться создать AVVideoComposition с активом AVPlayerItem, например. не создавать AVMutableComposition, AVMutableVideoComposition (актив: playerItem.asset, применяя CIFiltersWithHandler ... - person Tiko; 05.07.2017
comment
К сожалению, это не работает. Как ни странно, для одних видео это работает отлично, а для других нет. - person IHaveAQuestion; 05.07.2017
comment
Происходит странная вещь: большинство видео будут фильтроваться идеально, но как только я попытаюсь отфильтровать видео, которое затем исчезнет, ​​никакое видео не будет работать, включая те, которые работали раньше. Контроллер представления каждый раз создается с нуля, поэтому я понятия не имею, почему это может произойти. Есть ли у вас какие-либо идеи? - person IHaveAQuestion; 07.07.2017
comment
Что вы имеете в виду, что видео исчезает? - person Tiko; 07.07.2017
comment
Какую версию ОС вы используете? - person Tiko; 07.07.2017
comment
Видео становится невидимым, но звук продолжает воспроизводиться. Это бета-версия iOS 11. Я не уверен, что это ошибка или это поведение, которое продолжается. - person IHaveAQuestion; 07.07.2017
comment
Вы можете попробовать это с iOS 10? - person Tiko; 07.07.2017
comment
а может проблема в фильтре, можно попробовать через CIFilter? - person Tiko; 07.07.2017
comment
Это CIFilter, Filter - это просто мой собственный класс-оболочка. - person IHaveAQuestion; 08.07.2017
comment
Я имею в виду попробовать с родным фильтром - person Tiko; 08.07.2017
comment
Я думал, что CIFilter был родным фильтром? - person IHaveAQuestion; 08.07.2017
comment
Разве ты не написал кастомное ядро? - person Tiko; 08.07.2017
comment
Нет, я использовал готовые ("CIPhotoEffectChrome", "CIPhotoEffectNoir", ...) - person IHaveAQuestion; 08.07.2017
comment
Были другие проблемы с воспроизведением видео в iOS 11. - person allenh; 12.07.2017
comment
@AllenHumpreys, отчет об ошибке отправлен. Надеюсь, они прислушаются :) Однако им действительно нужна лучшая документация по этому поводу! - person IHaveAQuestion; 13.07.2017

Вместо того, чтобы предполагать, что изображение будет отфильтровано, сначала проверьте, соответствует ли filteredImage nil. Если нет, то request.finish(with: filteredImage, context: nil)

Однако, если это nil, вы должны request.finish(with: SomeError)

Это согласно документам.

person Daniel    schedule 07.07.2017

Что сработало для меня в конце:

private func filterVideo(with filter: Filter?) {
    guard let player = playerLayer?.player, let playerItem = player.currentItem else { return }

    let videoComposition = AVVideoComposition(asset: playerItem.asset, applyingCIFiltersWithHandler: { (request) in
        if let filter = filter {
            if let filteredImage = filter.filterImage(request.sourceImage) {
                let output = filteredImage.cropping(to: request.sourceImage.extent)
                request.finish(with: output, context: nil)
            } else {
                printError("Image not filtered")
                request.finish(with: RenderError.couldNotFilter)
            }
        } else {
            let output = request.sourceImage.cropping(to: request.sourceImage.extent)
            request.finish(with: output, context: nil)
        }
    })

    playerItem.videoComposition = videoComposition
}

Это функция filterImage для Filter, которая является просто приятной маленькой оболочкой для CIFilter:

func filterImage(_ ciImage: CIImage) -> CIImage? {
    guard let filter = ciFilter else { return nil }
    filter.setDefaults()
    filter.setValue(ciImage, forKey: kCIInputImageKey)
    guard let filteredImageData = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return nil }
    return filteredImageData
}
person IHaveAQuestion    schedule 11.06.2018
comment
Привет! Использование кода выше не приводит к вращению? - person Roi Mulia; 15.06.2018
comment
Он все еще вращается :\ - person Roi Mulia; 15.06.2018
comment
@RoiMulia, видео записывается устройством? - person IHaveAQuestion; 16.06.2018

Попробуйте этот код ниже, который работал для меня

// Grab the source track from AVURLAsset for example.
let assetV = YourAVASSET.tracks(withMediaType: AVMediaTypeVideo).last

// Grab the composition video track from AVMutableComposition you already made.
let compositionV = YourCompostion.tracks(withMediaType: AVMediaTypeVideo).last

// Apply the original transform.
if ((assetV != nil) && (compostionV != nil)) {
    compostionV?.preferredTransform = (assetV?.preferredTransform)!
}

А затем экспортируйте видео...

person sibin    schedule 08.11.2017
comment
Невозможно назначить свойство: «preferredTransform» — это свойство только для получения - person Yaroslav Dukal; 01.06.2018