3

I have a problem with CALayer. The point is that I want to save few UIViews on the movie. Basically I am trying to do it by CALayer. Everything is okay, the app export movie with layers but the layers with views are smaller than video frame.

I tried to scale layer manually but at all it is not look like on the screen. If I move UIView higher on movie it is going much higher.

Video has FullHd frame and layers of UIView depends of it how user place it on the screen.

The questions are: - How to render UIView Context with better retina quality?

  • How to scale layers to fit perfectly to the video.

There is the code:

VideoSaver.saveVideo(videoAsset!) { (composition, size) in
        ////////////////
        //Parent Layer//
        ////////////////

        let parentLayer = CALayer()
        parentLayer.frame = CGRectMake(0, 0, size.width, size.height)

        /////////
        //Sizes//
        /////////
        let xScaleValue = CGRectGetWidth(parentLayer.frame)/CGRectGetWidth(self.view.frame)
        let yScaleValue = CGRectGetHeight(parentLayer.frame)/CGRectGetHeight(self.view.frame)

        ///////////////
        //Video Layer//
        ///////////////
        let videoLayer = CALayer()
        videoLayer.frame = CGRectMake(0, 0, size.width, size.height)

        //////////////////
        //Captions Layer//
        //////////////////

        //Creating UIImage from Captions
        UIGraphicsBeginImageContext(CGSizeMake(self.captionsTextView.bounds.size.width, self.captionsTextView.bounds.size.height+20))
        let currentContext = UIGraphicsGetCurrentContext()

        CGContextTranslateCTM(currentContext, 0, 16)

        self.captionsTextView.layer.renderInContext(currentContext!)
        let captionTextViewImage = UIGraphicsGetImageFromCurrentImageContext()

        UIGraphicsEndImageContext()

        let captionsOverlayLayer = CALayer()
        captionsOverlayLayer.contents = captionTextViewImage.CGImage
        captionsOverlayLayer.frame = CGRectMake(self.captionsTextView.frame.origin.x * xScaleValue,
                                            (size.height - ((self.captionsTextView.frame.origin.y - 20) * yScaleValue)),
                                            CGRectGetWidth(self.captionsTextView.frame) * xScaleValue,
                                            (CGRectGetHeight(self.captionsTextView.frame)+20) * yScaleValue)

        captionsOverlayLayer.masksToBounds = true

        ///////////////////
        //TagFilter Layer//
        ///////////////////
        UIGraphicsBeginImageContext(self.tagView.bounds.size)
        self.tagView.layer.renderInContext(UIGraphicsGetCurrentContext()!)

        let tagViewImage = UIGraphicsGetImageFromCurrentImageContext()

        UIGraphicsEndImageContext()

        let tagOverlayLayer = CALayer()
        tagOverlayLayer.contents = tagViewImage.CGImage
        tagOverlayLayer.frame = CGRectMake(self.tagView.frame.origin.x * xScaleValue,
                                           (size.height - ((self.tagView.frame.origin.y) * yScaleValue)),
                                           CGRectGetWidth(self.tagView.frame) * xScaleValue,
                                           (CGRectGetHeight(self.tagView.frame)) * yScaleValue)
        ///////////////////////////////////////////////

        parentLayer.addSublayer(videoLayer)
        parentLayer.addSublayer(captionsOverlayLayer)
        parentLayer.addSublayer(tagOverlayLayer)

        ///////////////
        //Composition//
        ///////////////
        composition.animationTool = AVVideoCompositionCoreAnimationTool.init(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)
    }

I will be glad for help :)

Dzeremix
  • 446
  • 1
  • 5
  • 24

2 Answers2

1

If the problem is the resolution of the image as displayed by the layer, then:

  • This is your first mistake: UIGraphicsBeginImageContext. Always call UIGraphicsBeginImageContextWithOptions and a third argument of 0, or an explicit resolution.

  • Your second mistake is that you never set the CALayer's contentsScale.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • If I understand correctly I must add to every CALayer `contentScale = self.myView.layer.contentsScale`? I changed also `UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.captionsTextView.bounds.size.width, self.captionsTextView.bounds.size.height+20), false, 0.0)`. Unfortunately I do not know how to resize layers to the video size :( – Dzeremix May 31 '16 at 16:36
  • Well, the thing you're doing has been thoroughly covered here; see for example http://stackoverflow.com/questions/5997677/mix-video-with-static-image-in-calayer-using-avvideocompositioncoreanimationtool, and also tutorials like https://www.raywenderlich.com/30200/avfoundation-tutorial-adding-overlays-and-animations-to-videos – matt May 31 '16 at 16:49
1

At the end all UIViews that I want to have on the video I put to one UIView. From that UIView I am creating the layer and scaling it to video size. There is a code if someone will have the same problem:

VideoSaver.saveVideo(videoAsset!) 
{ 
    (composition, size) in
    ////////////////
    //Parent Layer//
    ////////////////

    let parentLayer = CALayer()
    parentLayer.frame = CGRectMake(0, 0, size.width, size.height)

    ///////////////
    //Video Layer//
    ///////////////
    let videoLayer = CALayer()
    videoLayer.frame = CGRectMake(0, 0, size.width, size.height)

    ///////////////////////
    //Effect Layout Layer//
    ///////////////////////
    UIGraphicsBeginImageContextWithOptions(self.effectLayoutView.bounds.size, false, 0.0)
    self.effectLayoutView.layer.renderInContext(UIGraphicsGetCurrentContext()!)

    let effectLayoutViewImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    let effectOverlayLayer = CALayer()
    effectOverlayLayer.contents = effectLayoutViewImage.CGImage

    effectOverlayLayer.frame = CGRectMake(0, 0, size.width, size.height)
    effectOverlayLayer.masksToBounds = true

    ///////////////////////////////////////////////

    parentLayer.addSublayer(videoLayer)
    parentLayer.addSublayer(effectOverlayLayer)

    ///////////////
    //Composition//
    ///////////////
    composition.animationTool = AVVideoCompositionCoreAnimationTool.init(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)
}
Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
Dzeremix
  • 446
  • 1
  • 5
  • 24
  • i am experiencing same problem, sub layer of parent layer not getting resized according to video layer size. Can you please help me ? and you have added parentLayer.addSublayer(videoLayer) parentLayer.addSublayer(effectOverlayLayer) why ? – Arpan Dixit Oct 22 '17 at 15:11