AVPreviewLayer contentsGravity not filling Layer - ios

I'm using AVFoundation to do some video recording and I've looked all over for how to get the video to aspect fill. I've read through avfoundation guide by apple and class referene for AVCaptureVideoPreviewLayer. I also read this question here AVFoundation camera preview layer not working . Here is my code
videoPreviewLayer.frame = captureView.frame
videoPreviewLayer.frame.origin = CGPoint(x: 0, y: 0)
videoPreviewLayer.backgroundColor = UIColor.redColor().CGColor
videoPreviewLayer.contentsGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer.masksToBounds = true
captureView.layer.addSublayer(videoPreviewLayer)
I put this in viewDidLayoutSubviews() so that I can get the correct size for captureView.frame which is the UIView my previewLayer is inside of. Any clue why aspectFill isn't working? As you can see from the red background the layer is the correct size but the contentsGravity isn't filling the layer.

Found my answer in this link AVCaptureVideoPreviewLayer . I needed to use videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill instead of videoPreviewLayer.contentsGravity = AVLayerVideoGravityResizeAspectFill

For me, the answer for my problem was provided by NSGangster in the question:
I put this in viewDidLayoutSubviews() so that I can get the correct size for captureView.frame which is the UIView my previewLayer is inside of.
I originally had the code in viewDidLoad() which meant the layer was resizing before the true bounds of the view was determined.
Thanks for sharing.

Related

AVfoundation Camera goes off screen Obj-C

I am working on scanning barcode ios App that uses AVFoundation
So i have created a square box with constraint using the interface builder. The square box is all good with the constraints. Perfectly fine.
i have this following code to add the avcapturelayer to the square box.
self.captureLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
[self.captureLayer setFrame:self.cameraPreviewView.layer.bounds];
[self.captureLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[self.cameraPreviewView.layer addSublayer:self.captureLayer];
the layer follows the leading space from the square box constraint, but not with the trailing. The new added AVlayer goes off the screen(to the right) while the square box itself is all good. What am I missing here?
thanks!
I think you should try to set self.captureLayer bounds/position instead of frame ?
Cheers!
This might be happening if you are setting the frame in viewDidLoad. If so, try doing it in viewWillAppear:instead.
This may solve your problem
CGRect bounds=view.layer.bounds;
captureLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
captureLayer.bounds=bounds;
captureLayer.position=CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
Or
as you are you are using AVLayerVideoGravityResizeAspectFill so it will go out of screen , you can use AVLayerVideoGravityResizeAspectFit instead.
you need to set clipToBound=YES; when using AVLayerVideoGravityResizeAspectFill (when using you View )
view.clipToBound=YES;
and than add Sublayer to view
view.layer.masksToBounds = YES;

What do I need for masking a UIImageView and how do I do it in Swift 3?

I was wondering what I would need if I wanted to use a mask image to get my UIImageView in a specific shape. From what I understand, to create a mask, I need to have an image with the shape of the mask all black on top of a white background. Something like this, for example:
First of all, is this sufficient to shape an image view, and if so, how do I do it in Swift 3? I can only find masking code that is either outdated or written in Objective-C. I've tried simply assigning the image above to an UIImageView and then assign the image view to the mask property of the UIImageView I want to shape, like so:
self.defaultImageView.mask = self.maskImageView
This didn't do anything. It just made self.maskImageView disappear (both image view's added through the storyboard and connected using IBOutlet properties). I'm sure I'm forgetting to do something. It can't be this simple. I would appreciate it if someone could help me out. Like I said, I put both image views on the exact same spot, on top of each other, in the storyboard.
UPDATE:
My first attempt to set the mask programmatically after deleting it from my storyboard.
let layer:CALayer = CALayer()
let mask:UIImage = UIImage(named: "Black-Star-Photographic-Agency")!
layer.contents = mask
layer.frame = CGRect(x: 0, y: 0, width: ((self.defaultImageView.image?.size.width)!), height: (self.defaultImageView.image?.size.height)!)
self.defaultImageView.layer.mask = layer
self.defaultImageView.layer.masksToBounds = true
The result was that the image view had completely disappeared and wasn't visible anymore. Am I doing something, am I forgetting something or both?
You should use a png image, which supports transparency, unlike jpg.
In Photoshop your image should look similar to this:
It doesn't matter if your shape is black or white. What matters is transparency of each pixel. Opaque area (black in this case) will be visible and transparent area will get trimmed.
Edit:
You should not create mask view from storyboard if you do so. It is not going to be a part of your view hierarchy. Just add it programmatically like this:
let maskView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
maskView.image = UIImage(named: "mask")
imageView.mask = maskView
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
maskView.frame = imageView.bounds
}
Output:
Here is a test project to show how it's working.
Also if you're using a custom frame/image and run into the mask not showing properly, try setting the content mode of the mask:
maskView.contentMode = .scaleAspectFit

Video stretch issue iOS Swift

I have an application with Swift. I want to play some video by using moviePlayer. I am using the following code.
let url = NSURL.fileURLWithPath(path)
moviePlayer = MPMoviePlayerController(contentURL: url)
// moviePlayer?.controlStyle = .None
if let player = moviePlayer {
player.view.frame = CGRect(x: 20, y: 165, width: widthVideoView, height: heightVideoView)
player.prepareToPlay()
player.scalingMode = .AspectFill
self.view.addSubview(player.view)
}
Playing video is fine no issues. But for now my video is stretched . I think this is happening because of
player.scalingMode = .AspectFill
My screenshot like this. .
So I change to
player.scalingMode = .AspectFit
Then my screen like this (top bottom with black screen.) How can I handle this.
Sorry for the edited answer , If you read the docu : https://developer.apple.com/library/prerelease/ios/documentation/MediaPlayer/Reference/MPMoviePlayerController_Class/index.html#//apple_ref/c/tdef/MPMovieScalingMode
.aspectFit: will create the blackBar because it keeps the aspect ratio of the video
.aspectFill , will act like a zoom on the (there is no black bar but the video get cut)
.Fill will stretch the video to fit (I think you want this one or change the view to follow the aspect ratio ).
If you don't respect the original aspect ratio :It will always stretch or have the black bars too fill .
So you can change the scalingMode to .Fill or change the view to respect the aspect ratio

AVSampleBufferDisplayLayer: change videoGravity animated

In my project I am using AVSampleBufferDisplayLayer and AVPlayerLayer. Bot of them have similar interface and videoGravity property.
When I change AVPlayerLayer videoGravity property video is re-sized immediately with animation effect. With AVSampleBufferDisplayLayer videoGravity nothing happen till I change device orientation and then video is re-sized without animation.
How to change videoGravity of AVSampleBufferDisplayLayer to behave similar to the AVPlayerLayer?
The only solution I've found is to reinitialize the AVSampleBufferDisplayLayer for example:
var displayBufferLayer: AVSampleBufferDisplayLayer?
...
func reinitBufferLayer(videoGravity: AVLayerVideoGravity) {
displayBufferLayer?.flush()
displayBufferLayer?.stopRequestingMediaData()
displayBufferLayer?.removeFromSuperlayer()
let bufferLayer = AVSampleBufferDisplayLayer()
bufferLayer.frame = view.bounds
bufferLayer.videoGravity = videoGravity // (i.e. .resizeAspectFill)
bufferLayer.isOpaque = true
view.layer.insertSublayer(bufferLayer, at: 0)
self.displayBufferLayer = bufferLayer
}

AVCaptureVideoPreviewLayer frame in Swift

I'm trying to get the video output on my screen in Swift. But the screen stays completely white. I found this tutorial in ObjC and I followed it (only in Swift style syntax).
In there there is a line previewLayer.frame = myView.bounds;
But the field .frame seems to be read only in swift. And I think this might be why I don't see anything on the screen.
How can I set the frame for the previewLayer in Swift?
I see three points in that tutorial where you could end up not displaying the preview, and thus getting a white screen. Below are the Obj-C and Swift counterparts.
1) You might have missed adding the input to the capture session:
// [session addInput:input];
session.addInput(input)
2) You might not have initialized the preview layer's bounds to that of your view controller:
// UIView *myView = self.view;
// previewLayer.frame = myView.bounds;
previewLayer.frame = self.view.bounds
3) You might not have added the preview layer as a sublayer of your view:
// [self.view.layer addSublayer:previewLayer];
self.view.layer.addSublayer(previewLayer)

Resources