I have now fully setup the ability to record video using the AVFoundation framework and this is all fine but now I am looking to add an overlay during the record (also visible on the AVCaptureVideoPreviewLayer Layer)
I can add this overlay UIView object to the VideoPreviewLayer but I am struggling how to get the same view to be on the recorded video.This UIView could contain anything from UILabels to UIImageViews.
I am not sure if this is the thing you are looking for but i guess you can use Brad Larson's GPU library,there is a class called GPUImageElement which lets you add overlays and views.Please check out the examples,especially the one called Filter showcase and scroll to something called UIElement.
Here is some sample code:
else if (filterType == GPUIMAGE_UIELEMENT)
{
GPUImageAlphaBlendFilter *blendFilter = [[GPUImageAlphaBlendFilter alloc] init];
blendFilter.mix = 1.0;
NSDate *startTime = [NSDate date];
UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 240.0f, 320.0f)];
timeLabel.font = [UIFont systemFontOfSize:17.0f];
timeLabel.text = #"Time: 0.0 s";
timeLabel.textAlignment = UITextAlignmentCenter;
timeLabel.backgroundColor = [UIColor clearColor];
timeLabel.textColor = [UIColor whiteColor];
uiElementInput = [[GPUImageUIElement alloc] initWithView:timeLabel];
[filter addTarget:blendFilter];
[uiElementInput addTarget:blendFilter];
[blendFilter addTarget:filterView];
__unsafe_unretained GPUImageUIElement *weakUIElementInput = uiElementInput;
[filter setFrameProcessingCompletionBlock:^(GPUImageOutput * filter, CMTime frameTime){
timeLabel.text = [NSString stringWithFormat:#"Time: %f s", -[startTime timeIntervalSinceNow]];
[weakUIElementInput update];
}];
}
You want to overlay UIView's, but if you don't mind using CALayers you could
add your overlay after export using AVAssetExportSession's AVVideoComposition property. It has a property, AVVideoCompositionCoreAnimationTool *animationTool which lets you add animating CALayers to your output, although I think you're out of luck if your overlay's appearance can't be described by CABasicAnimation. Your example of a display heading may be possible, although I imagine something as simple as a current time counter would not. If you can live with this restriction, the WWDC 2010 code sample 'AVEditDemo' is a good starting point.
If you need more control, you could manually render the overlay UIView onto the capture frames, using [view.layer renderInContext:contextToThenRenderToFrame] and then write these frames to file using AVAssetWriter (once you capture frames to memory you can no longer use AVCaptureMovieFileOutput).
Warning: the frames you are capturing may not arrive at a uniform rate and depend on ambient lighting and even system load. If your overlay changes at a higher rate than the capture video, then you will need to repeat frames in the second solution. This is handled for you by AVVideoComposition in the first solution.
P.S. Solution two is fiddly, but without going into details, iOS7 seems to have made this a lot easier.
you can find it in AVFoundation Programming Guide in the editing part
There is a section that deals with overlay of image. I will try to put up a sample code /project.
To figure out your problem there are two very detailed explanations on the raywenderlich tutorial site. It is a two part tutorial.
The first one is here: http://www.raywenderlich.com/13418/how-to-play-record-edit-videos-in-ios
The second one is here: http://www.raywenderlich.com/30200/avfoundation-tutorial-adding-overlays-and-animations-to-videos
Good Luck and Hope This Helps!!
Related
How to Implement this kind of animation in iOS objective c.
Currently I have implemented "CABasicAnimation" for this but still not able to achieve this very smoothly .
any suggestion ??
Thank you
You'll need to use Core Image with a combination of different distortion filters.
You can then animate the intensity of the filters over time.
I don't think that with CABasic animation you can obtain that. Probably is a little "movie" made by combination of CIFilterd images (check CIFilter class in the doc). Another way around (but I'm writing as I'm thinking) is mask the image somehow with a CAShapeLayer and use CAAnimation on it.
As Leena pointed out this is a simple loop of images.
Create your frames outside Xcode and then animate them this way:
NSArray *frames = [NSArray arrayWithObjects:[UIImage imageNamed:#"firstFrame.png"],
[UIImage imageNamed:#"secondFrame.png"],
// add other frames
, nil];
UIImageView *animatedImage = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
animatedImage.animationImages = frames;
animatedImage.animationDuration = 3.0f; // seconds
animatedImage.animationRepeatCount = 0; // 0 = loops forever
[animatedImage startAnimating];
[self.view addSubview:animatedImage];
[animatedImage release];
I'm trying to make an animation that leaves a trail behind it for the iPhone. Any ideas how to do that?
THank you!
You can use CAEmitterLayers to do particle effects. iOS 5 only though.
http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CAEmitterLayer_class/Reference/Reference.html
The Cocoa Touch framework does not have any in-built features that let you do this, that I know of.
If you want to make one yourself, you would have to construct the animation frame-by-frame. Each frame would be a snapshot of what the screen would look like at that point in the animation. They can displayed one after the other with UIImageView.
NSArray *frames = [NSArray arrayWithObjects:frame1, frame2,...., nil];
UIImageView *animateFrames = [UIImageView alloc] init];
animateFrames.animationImages = frames;
animateFrames.animationDuration = DESIRED_DURATION;
animateFrames.repeatCount = 1;
[animateFrames startAnimating];
I have a grid of 30 UIButtons and potentially even more, subclassed to be rendered using layers: a base CALayer, a CAShapeLayer, a CAGradientLayer and a CATextLayer. I am trying to minimize the overall time required to render/display the buttons when the corresponding xib file is loaded. If I simply setup each button in turn in viewDidLoad, the time required for the view to appear is about 5-6 seconds, which is clearly too much.
In order to speed-up the buttons setup, I am using Grand Central Dispatch as follows. In viewDidLoad, I setup each button layers using dispatch_async on the global queue (adding to the base layer the shape and the gradient layers), so that the buttons can be rendered in a different thread. A the end of the block, the CATextLayer is added to the gradient layer.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
CGRect base_bounds = CGRectMake(0, 0, self.layer.bounds.size.width, self.layer.bounds.size.height - self.layer.bounds.size.height * 0.10f);
CGPoint move_point = CGPointMake(0.0f, base_bounds.size.height * 0.10f);
self.layer.masksToBounds = NO;
baseLayer = [CALayer layer];
baseLayer.cornerRadius = 10.0;
baseLayer.shadowOffset = CGSizeMake(0.0f, 2.0f);
baseLayer.shadowOpacity = 1.5f;
baseLayer.shadowColor = [UIColor blackColor].CGColor;
baseLayer.shadowRadius = 2.5f;
baseLayer.anchorPoint = CGPointMake(0.5f, 0.5f);
baseLayer.position = move_point;
CAShapeLayer *shape = [CALayer layer];
shape.bounds = base_bounds;
shape.cornerRadius = 10.0;
shape.anchorPoint = CGPointMake(0.0f, 0.0f);
shape.position = move_point;
shape.backgroundColor = [UIColor darkGrayColor].CGColor;
gradient = [CAGradientLayer layer];
gradient.anchorPoint = CGPointMake(0.0f, 0.0f);
gradient.position = CGPointMake(0.0f, 0.0f);
gradient.bounds = base_bounds;
gradient.cornerRadius = 10.0;
gradient.borderColor = [UIColor colorWithRed:0.72f
green:0.72f
blue:0.72f
alpha:1.0].CGColor;
gradient.borderWidth = 0.73;
gradient.colors = [NSArray arrayWithObjects:
(id)[UIColor whiteColor].CGColor,
(id)[UIColor whiteColor].CGColor,
nil];
[baseLayer addSublayer:shape];
[baseLayer addSublayer:gradient];
[self.layer addSublayer:baseLayer];
[textLayer setBounds:gradient.bounds];
[textLayer setPosition:CGPointMake(CGRectGetMidX(textLayer.bounds), CGRectGetMaxY(textLayer.bounds) - 6)];
[textLayer setString:self.titleLabel.text];
[textLayer setForegroundColor:[UIColor blackColor].CGColor];
[gradient addSublayer:textLayer];
});
This approach reduce the overall time to about 2-3 seconds. I am wondering if anyone can suggest a faster way to render the buttons. Please note that I am not interested to any solution which discards the use of layers.
Thank you in advance.
Perhaps I'm missing the point but wouldn't you be better off overriding the UIButton drawRect: method and doing your drawing in CoreGraphics (CG) things would be drawn A LOT faster than seconds, you can easily do gradients, text, images with the CG API. If I understand correctly you have 4 layers per button and 30+ buttons in the same view (120+ layers)? If so, I don't think you are meant to draw so many layers (rendering/blending all of them individually would explain the huge render time). Another possibility would be to have 4 big layers, for all of the buttons.
Here's my idea,
Separate your CALayers from UIButton - UIKit won't allow anything on the background thread.
When you have an opportunity in a screen preceding the button grid, use [buttonGridViewController performSelectorInBackground:renderCALayers] to render your CALayers in the background.
When you load your button grid in viewDidLoad, overlay UIButtons with type UIButtonTypeCustom, and backgroundColor = [UIColor clearColor] over the top of your 'button' CALayers (make sure to call bringSubviewToFront on the UIButtons so they get the touch events).
If you don't know how many buttons you are rendering, you might want to pre-render the maximum number you might display.
If you have any questions please comment.
EDIT:
A few related items,
What to replace a UIButton to improve frame rate?
UI design - Best way to deal with many UIButtons
How to get touch event on a CALayer?
I believe the only way you'll get quicker load from this point is to either replace or remove UIButtons altogether and intercept touch events with another approach. That way all rendering can be done in a background thread before the view is presented.
Have you considered using NSOperation to generate your button's layers? In your code, you'd instantiate an operation queue and then iterate through each button, firing an operation (passing the respective parameters required to generate that button's layer) into the queue as each button is initiated.
Within your NSOperation, generate your layer using CoreGraphics, then pass the layer back to the button that initiated the operation (in this case, the destination button) via your completion handler (be sure to call that on the main thread). This should give you pretty good performance as your buttons layer's will be rendered off the main thread using CoreGraphics, then passed back to UIKit for presentation only after the layer has been rendered.
NSOperation sits atop GCD, has a marginal overhead but provides benefits in your case, such as the ability to set dependent prerequisites and prioritization.
Also, I'm pretty sure you shouldn't call UIColor in the GCD block you shared.
Hope this is helpful!
I am trying to rotate my object like shakes dice.
please suggest simplest way to implement it in my iphone application.
Any kind of sample code or documentation.
http://www.amazon.com/OpenGL-SuperBible-Comprehensive-Tutorial-Reference/dp/0321498828
If you want to do this without using OpenGL you can use a UIImageView and the set a series of animation images. By creating a series of images you can create a "rolling dice" effect. Here is an example of how to do this:
// create a UIImageView
UIImageView *rollDiceImageMainTemp = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"rollDiceAnimationImage1.png"]];
// position and size the UIImageView
rollDiceImageMainTemp.frame = CGRectMake(0, 0, 100, 100);
// create an array of images that will represent your animation (in this case the array contains 2 images but you will want more)
NSArray *savingHighScoreAnimationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"rollDiceAnimationImage1.png"],
[UIImage imageNamed:#"rollDiceAnimationImage2.png"],
nil];
// set the new UIImageView to a property in your view controller
self.viewController.rollDiceImage = rollDiceImageMainTemp;
// release the UIImageView that you created with alloc and init to avoid memory leak
[rollDiceImageMainTemp release];
// set the animation images and duration, and repeat count on your UIImageView
[self.viewController.rollDiceImageMain setAnimationImages:savingHighScoreAnimationImages];
[self.viewController.rollDiceImageMain setAnimationDuration:2.0];
[self.viewController.rollDiceImageMain.animationRepeatCount:3];
// start the animation
[self.viewController.rollDiceImageMain startAnimating];
// show the new UIImageView
[self.viewController.view addSubview:self.rollDiceImageMain];
You can modify the creation and setup including the size, position, number of images, the duration, repeat count, etc as needed. I've used this feature of UIImageView to create simple animation effects and it works great!
Please let me know if you try this and if it works for your needs. Also, post any follow up questions.
Bart
nice your example, but do you think it's possible to rotate / control the rotation with touch gesture ?
I have an animated gif file that I want to use in my iPhone application, but the animation doesn't work. Does anybody know how to fix it?
If you have a serie of images you want to animate you can easily do it with UIImageView:
UIImage *blur5 = [UIImage imageNamed:#"blur5.png"];
UIImage *blur6 = [UIImage imageNamed:#"blur6.png"];
self.imageView.animationImages = [[NSArray alloc] initWithObjects:blur5, blur6, nil];
self.imageView.animationRepeatCount = 5;
[self.imageView startAnimating];
I found this easier than trying to use UIWebView.
UIWebView does not properly display all GIF content. You need to use a UIImageView, but the iPhone OS does not support animated GIFS and only displays the first frame.
So you need to extract all of the other frames first.
Crude example code here:
http://pliep.nl/blog/2009/04/iphone_developer_decoding_an_animated_gif_image_in_objc
You can use source at http://blog.stijnspijker.nl/2009/07/animated-and-transparent-gifs-for-iphone-made-easy/
It has a GIF decoder that you can use directly to get solution.
I successfully used it. But it have some problems with transparency.
This can be achieved by this piece of code:
NSArray * imageArray = [[NSArray alloc] initWithObjects:[UIImage imageNamed:#"1.png"], [UIImage imageNamed:#"2.png"], nil]; //this will be the frames of animation images in sequence.
ringImage = [[UIImageView alloc]initWithFrame:CGRectMake(100,200,600,600)];
ringImage.animationImages = imageArray;
ringImage.animationDuration = 1.5;//this the animating speed which you can modify
ringImage.contentMode = UIViewContentModeScaleAspectFill;
[ringImage startAnimating];//this method actually does the work of animating your frames.
I know its an old thread..but may be helpful for someone..:)
One other option is to decode the gif in your application, and then "frame serve" it to a OpenGL object. This way is less likely to run out of memory for large gifs.