【问题标题】:Video rotated after applying AVVideoComposition应用 AVVideoComposition 后旋转的视频
【发布时间】:2019-09-20 14:13:49
【问题描述】:

AVVideoComposition 应用到我的AVPlayerItem 后,我应用的过滤器确实有效,但视频在AVPlayerLayer 中旋转。

我知道问题不在于过滤后的帧,因为如果我在 UIImageView 中显示该帧,则该帧将 100% 正确呈现。

视频正确显示直到我申请了videoComposition。在AVPlayerLayer 上设置videoGravity 没有帮助。

视频顺时针旋转 90º 并在图层中拉伸。

基本上,在AVPlayerItem 通过AVMutableVideoComposition 馈送之前,视频在AVPlayerLayer 中完美显示。一旦发生这种情况,视频将旋转 -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
    }
}

【问题讨论】:

  • 您的 videoComposition 是自定义的还是您正在使用 `AVVideoComposition(_:, appliedCIFiltersWithHandler:)?
  • @Tiko,我用applyingCIFiltersWithHandler
  • 尝试删除行:videoCompositionTrack?.preferredTransform = videoAssetTrack!.preferredTransform
  • @ninjaproger,这使视频消失了,尽管它在某些时候奇怪地适用于某些视频。有时它会使视频暂停而不是过滤器。
  • @IHaveAQuestion 这只是发生在垂直或水平视频中,还是两者兼而有之?测试一下,让我知道。

标签: ios swift avfoundation cifilter avvideocomposition


【解决方案1】:

AVVideoComposition 的渲染大小有问题。您应该在AVMutableVideoCompositionInstruction 上应用转换(即Rotatetranslate 转换)。

我已经在 Objective-c 中完成了它,并且正在发布我的代码。您可以将语法转换为 Swift

Objective-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];
});
}];

对于 斯威夫特 看到这个答案Click here

此外,您还可以尝试通过对其应用旋转变换来旋转视频层。

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

[_playerLayer setAffineTransform:CGAffineTransformMakeRotation(degreeToRad‌​ian(degree))]

【讨论】:

  • videoInstruction 设置为videoComposition 使得组合的处理程序不会被调用。我猜我错过了一些明显的东西。有任何想法吗?这让我发疯了!
  • 另外,我不是在尝试合并视频:我是在尝试过滤一项资产。
  • 请大家帮忙!!我会喜欢能够奖励你赏金!!!
  • 整个视频被拉伸或某些资产有这个问题??如果所有视频都被拉伸,那么您可以将视频旋转到所需的角度[_playerLayer setAffineTransform:CGAffineTransformMakeRotation(degreeToRadian(degree))];@IHaveAQuestion
  • 弧度的度数是#define degreeToRadian(x) (M_PI * x / 180.0)
【解决方案2】:

如果你想玩AVMutableCompostion,你应该将AVAssetTrackpreferredTransform设置为AVMutableCompositionTrackpreferredTransform

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

【讨论】:

  • 不幸的是,这不起作用。视频还是一团糟
  • 您可以尝试使用AVPlayerItem 的资产创建AVVideoComposition,例如不创建AVMutableComposition, AVMutableVideoComposition(asset: playerItem.asset, applicationsCIFiltersWithHandler ...
  • 不幸的是,这不起作用。奇怪的是,它适用于某些视频,但不适用于其他视频。
  • 发生了一件奇怪的事情:大多数视频都可以完美过滤,但是一旦我尝试过滤然后消失的视频,no 视频就可以工作了——包括以前可以工作的视频.视图控制器每次都从头开始重新实例化,所以我不知道为什么会发生这种情况。你有什么想法吗?
  • 视频消失了是什么意思?
【解决方案3】:

不要假设图像将被过滤,而是首先检查filteredImage 是否为nil。如果没有,那么request.finish(with: filteredImage, context: nil)

但是,如果是nil,则必须request.finish(with: SomeError)

这是根据文档。

【讨论】:

    【解决方案4】:

    最后对我有用的是什么:

    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
    }
    

    这是FilterfilterImage 函数,它只是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
    }
    

    【讨论】:

    • 你好,使用上面的代码旋转不会发生吗?
    • 它还在旋转:\
    • @RoiMulia,视频是设备录制的吗?
    【解决方案5】:

    试试下面这对我有用的代码

    // 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)!
    }
    

    然后继续导出您的视频...

    【讨论】:

    • 无法分配给属性:“preferredTransform”是一个只能获取的属性
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    • 2011-07-08
    相关资源
    最近更新 更多