I was able to capture video frames from the camera using AVCaptureSession according to http://developer.apple.com/iphone/library/qa/qa2010/qa1702.html. However, it seems that AVCaptureScreen captures frames from the camera without showing the camera stream on the screen. I would like to also show camera stream just like in UIImagePicker so that the user knows that the camera is being turned on and sees what the camera is pointed at. Any help or pointer would be appreciated!
AVCaptureVideoPreviewLayer is exactly what you're looking for.
The code fragment Apple uses to demonstrate how to use it is:
AVCaptureSession *captureSession = <#Get a capture session#>;
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
UIView *aView = <#The view in which to present the layer#>;
previewLayer.frame = aView.bounds; // Assume you want the preview layer to fill the view.
[aView.layer addSublayer:previewLayer];
Related
I have an requirement to capture iPhone screen when my app is in foreground . I have used UIGraphicsGetImageFromCurrentImageContaxt() for this it works in most of synerio but fails when video is playing by using MPMoviePlayerViewController or AVPlayer and gives back black image with player control.
Probabely My guess is MPMoviePlayerViewController rendering frames using OpenGl and method UIGraphicsGetImageFromCurrentImageContaxt() is not able to capture the image ??
I am missing something or is there any alternative soln available to capture iPhone Screen while app is in foreground ??
There is not any easy solution(i.e. might be I don't know) for this but you need to figure out. Here I have described possible solutions. When you will try simply rendering the view on context then it will give blank screen in place of player and other things will be as it is.
Possible solutions that I know
Private API
You can use UIGetScreenImage() function to capture whole screen of device including player and its control. This is the way you can get the image of player with view easily.
Note: Some buddies are saying use of this function may cause rejection of application(I have never used for app store's app so I don't have much experience about it :)).
Second way.
If you want to get image of player with the other things that resides in main view or iPhone screen then basic idea is to capture two images(i.e. One of movie player and another of whole screen like you are doing using UIGraphicsGetImageFromCurrentImageContext) and combine them as a one image using some proper calculations.In this way you need to do some calculations so accuracy is depend upon your calculations.
Here I am preferring to use AVPlayerinstead of MPMoviePlayer not sure I am true or not but as I have feel that AVPlayer providing exact frame at certain time(Good accuracy. Exact image that is showing on the screen.)
Please set the gravity mode of AVPlayer to AVLayerVideoGravityResizeAspect. This mode will preserver aspect ratio and fit within layers bounds(i.e. This option is default). You can set this way.
playerLayer.videoGravity = setVideoFillMode:AVLayerVideoGravityResizeAspect;
where playerLayer is the object of AVPlayerLayer.
Now get the image of AVPlayerLayer
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc]
initWithAsset:asset];
imageGenerator.appliesPreferredTrackTransform=YES;
/*AVAssetImageGenerator will scale images such that they fit within the defined bounding box. Images will never be scaled up. So provide this should be the size of AVPlayer. */
imageGenerator.maximumSize=CGSizeMake(400, 400);
CGImageRef imgRef = [imageGenerator copyCGImageAtTime:storedCMTime actualTime:NULL error:NULL];
[imageGenerator release];
Please note that this generated image might be not exact size for what you are looking for or as you have exactly provided in maximum size because as I mentioned in above comment here image will be never scaled up. if image is not the exact size for what you are looking then use some image utility function to scale the image as per the size of your player but don't spoil Aspect ration because we have set the AVPLayer's mode to AVLayerVideoGravityResizeAspect.store this image temporary.
Now Capture the image of View using UIGraphicsGetImageFromCurrentImageContext(i.e. like you are doing currently). Draw image of AVplayer that we have captured and stored over exactly the black area that is showing on your view(i.e. You need to do some trial and error to get the exactly same).
Here I have tried to cover all the main points that may cause panic or not immediately obvious.
Update:
Screen capturing solutions from APPLE: http://developer.apple.com/library/ios/#qa/qa1703/_index.html#//apple_ref/doc/uid/DTS40010193
If you would like to go with MPMoviewPlayerController then it's okay. Get the thumbnail using code shows by #Kuldeep. And combine the Both view's image and Thumbnail using Image Masking exactly explain here : Link
I would rather suggest to use below snippet, may it would be more easy to use. You just need to pass time factor at which you need to capture the video.
float timeFactor = 60.0;
CGRect rect = CGRectMake(self.view.center.x, self.view.center.y, 100.0, 100.0)
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:videoURL];
UIImage *thumbnail = [player thumbnailImageAtTime:timeFactor timeOption:MPMovieTimeOptionNearestKeyFrame];
UIImageView *imagV=[[UIImageView alloc]initWithImage:thumbnail];
[imagV setFrame:rect];
Well, it seems I've found a solution to capture frames in a AVPlayer:
- (UIImage*) captureFrame {
AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:[self.player.currentItem asset]];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 5) {
[imageGenerator setRequestedTimeToleranceAfter:kCMTimeZero];
[imageGenerator setRequestedTimeToleranceBefore:kCMTimeZero];
}
CGImageRef ref = [imageGenerator copyCGImageAtTime:self.player.currentItem.currentTime actualTime:nil error:nil];
UIImage *img = [UIImage imageWithCGImage:ref];
CGImageRelease(ref);
return img;
}
I have an iPhone app where I load an UIImagePickerController onto a UIViewController. I then have a custom view on top of the camera. Now when the user takes a photo it is loaded onto a UIImageView which presents it to the user asking if you want to use that photo or take another (removing the image from the UIImageView). This works perfectly.
Now If the user has just recorded a video I wanted to take a snapshot preview somewhere in the video and present it as a static image in the same UIImageView. I do this with the following code:
MPMoviePlayerController *videoPlayer = [[MPMoviePlayerController alloc] init];
videoPlayer.shouldAutoplay = NO;
[videoPlayer setContentURL:[info valueForKey:UIImagePickerControllerMediaURL]];
UIImage *videoScreenShot = [videoPlayer thumbnailImageAtTime:(videoPlayer.duration/2.0) timeOption:MPMovieTimeOptionNearestKeyFrame];
photoPreview.image = videoScreenShot;
[videoPlayer release]
This works as intended. The problem is if I want to take another video. When I call:
[videoPlayer setContentURL:[info valueForKey:UIImagePickerControllerMediaURL]];
The camera shutter closes and the camera is seemingly dismissed. Trying to take a picture or recording video gives me:
UIImagePickerController: ignoring request to take picture; camera is not yet ready.
UIImagePickerController: ignoring request to start video capture; camera is not yet ready.
I've tried calling the following after, which has no effect:
myImagePicker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType: UIImagePickerControllerSourceTypeCamera];
[myImagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
I tried adding the UIImagePickerController to the UIIViewController again, which caused some fantastic freeze-ups.
Finally I tried commenting out the UIViewController's [super didReceivedMemoryWarning] line of didReceivedMemoryWarning. Which also had no effect.
I'm guessing MPMoviePlayerController takes over something UIImagePicker also needs. How do I give it back?
I would suggest setting your MPMoviePlayerController to nil before showing the UIImagePickerController, or maybe taking its view out of the interface temporarily. Both of these contain a movie player view, but there can be only one movie player view at a time in your application's interface. Thus they can interfere with each other, and that might be what's happening to you.
i have some mp4 files which are to be played in my iPad app.. I am able to do that quite well. right now i have a play button on blank black space in my app and the video plays after i tap the play button. The user will only come to know the content of the video after playing it.. But, i want the user to know about the video before playing itself . instead of the default black screen i want to show the video starting screen to make the video more interesting. To put it in simple words, i want my video space to be similar to youtube... IS there any way i which this can be done?? Thanks
Subclass MPMoviePlayerController (or however you're playing your video.. if you already use a custom class just add the code in there) and in viewDidAppear initialise a UIImageView with frame size equal to self.view.bounds using whatever background image you want for the loading screen. Add the UIImageView as a subview of self.view and call sendSubviewToBack: on it. When the player is ready to play, it will start drawing video frames on top of your subview, and you should not see it again.
- (void)viewDidAppear
{
UIImageView *loadingImage = [[UIImageView alloc] initWithImage:myImage];
[loadingImage setFrame:self.view.bounds];
[self.view addSubview:loadingImage];
[self.view sendSubviewToBack:loadingImage];
[super viewDidAppear];
}
I am doing video recording from my iPhone 4 application program. I want to do an enhancement same like photo booth's 3 .. 2 .. 1 count down prior to taking video recording. Is it possible to do that on my iPhone program programmatically? If YES, how and if NO why? Please advise.
Thank you in advance.
Yes.
All you really need to do is draw the 3,2 and 1 on the screen on top of your AVCapturePreviewLayer.
Here's is Apple's code from documentation:
AVCaptureSession *captureSession = <#Get a capture session#>;
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
UIView *aView = <#The view in which to present the layer#>;
previewLayer.frame = aView.bounds; // Assume you want the preview layer to fill the view.
[aView.layer addSublayer:previewLayer];
so your part is simple:
[[aView superview] addSubview:countdownView];
Inside countdown view, you can have a custom draw method or just add UILabels. Lots of options on actually doing the countdown. You can use NSTimers to change the labels or even UIView animations with callbacks.
I'm trying to emulate the animation seen in the default camera app, where a snapshot of the cameras viewfinder is animated into the corner of the apps display.
The AVCaptureVideoPreviewLayer object that holds the key to solving this problem isn't very open to these requirements: trying to create a copy of it in a new layer with ..
- (id)initWithLayer:(id)layer
.. returns an empty layer, without the image snapshot, so clearly there is some deeper magic going on here.
Your clues/boos are most welcome.
M.
facing the same woes, from a slightly different angle.
Here are possible solutions, that none are too great IMO:
You can add to an AVCaptureSession both an AVCaptureStillImageOutput and an AVCaptureVideoDataOutput. When you set the sessionPreset to AVCaptureSessionPresetHigh you'll start getting frames by the API, and when you switch to AVCaptureSessionPresetPhoto you can take real images. So right before taking the picture, you can switch to video, get a frame, and then return to camera. Major caveat is that it takes a "long" time (couple of seconds) for the camera to switch between the video camera and picture camera.
Another option would be to use only the camera output (AVCaptureStillImageOutput), and use UIGetScreenImage to get a screen capture of the phone. You could then crop out the controls and leave only the image. This gets complicated if you're showing UI controls over the image. Also, according to this post, Apple started rejecting apps that use this function (it was always iffy).
Aside from these I also tried playing with AVCaptureVideoPreviewLayer. There's this post to save a UIView or CALayer to a UIImage. But it all produces clear or white images. I tried accessing the layer, the view's layer, the superlayer, the presentationLayer, the modelLayer, but to no avail. I guess the data in AVCaptureVideoPreviewLayer is very internal, and not really part of the regular layer infrastructure.
Hope this helps,
Oded.
I think you should add an AVCaptureVideoDataOutput to the current session with:
AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
videoOutput.videoSettings = #{ (NSString *)kCVPixelBufferPixelFormatTypeKey : #(kCVPixelFormatType_32BGRA) };
[session addOutput:videoOutput];
dispatch_queue_t queue = dispatch_queue_create("MyQueue", NULL);
[videoOutput setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);
Then, implement the delegate method below to get your image snapshot:
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
// Add your code here that uses the image.
dispatch_async(dispatch_get_main_queue(), ^{
_imageView.image = image;
});
}
This will consume memory and reduce the performance of the app. To improve, you can also optimize your AVCaptureVideoDataOutput with:
videoOutput.minFrameDuration = CMTimeMake(1, 15);
You can also use alwaysDiscardsLateVideoFrames.
there are 2 ways to grab frames of the preview.. AVCaptureVideoDataOutput & AVCaptureStillImageOutput :)
is your capture session is setup to grab video frames, then make your layer with the cgimage from a chosen frame. if it's setup for stills, wait until getting your still image and make your layer from a scaled down version of that cgimage. if you don't have an output on your session yet, you'll have to add one i think.
Starting in iOS 7, you can use UIView::snapshotViewAfterScreenUpdates to snapshot the UIView wrapping your AVCaptureVideoPreviewLayer. This is not the same as UIGetScreenImage, which will get your app rejected.
UIView *snapshot = [self.containerView snapshotViewAfterScreenUpdates:YES];
Recall the old-school way of turning a view into an image. For some reason it worked on everything except for camera previews:
UIGraphicsBeginImageContextWithOptions(self.containerView.bounds.size, NO, [UIScreen mainScreen].scale);
[self.containerView drawViewHierarchyInRect:self.containerView.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();