需求
由于需求的原因要做到视频的实时渲染但是并不改变原视频,也就是说不是针对视频本身去加滤镜,所以就一直在网上找方法,网上大神说是要拿到视频的每一帧图片进行渲染。接下来就开始动手。
1.拿到视频每一帧
在AVFoundation框架里有一个类AVPlayerItemVideoOutput,用来获取视频输出的每一帧,具体看代码
AVPlayerItem *item = [AVPlayerItem playerItemWithURL:videoUrl];
_videoOutPut = [[AVPlayerItemVideoOutput alloc] initWithOutputSettings:nil];
_player = [[AVPlayer alloc] initWithPlayerItem:item];
[self.player.currentItem addOutput:_videoOutPut];
上面初始化了一个item和videoOutPut,并且把videoOutPut加到了item上面,现在我们要在每一帧的时候去获取buffer。
CMTime itemTime = [_videoOutPut itemTimeForHostTime:CACurrentMediaTime()];
if ([_videoOutPut hasNewPixelBufferForItemTime:itemTime]) {
CVPixelBufferRef pixelBuffer = [_videoOutPut copyPixelBufferForItemTime:itemTime itemTimeForDisplay:nil];
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
CVBufferRelease(pixelBuffer);
}else{
}
这样我们就获取到了视频每一帧的CIImage,现在我们就可以加上滤镜了。
2.加滤镜
加滤镜就简单用了系统的对比度,亮度之类的具体的大家可自行配置好看的滤镜。
CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"];
[filter setValue:ciImg forKey:kCIInputImageKey];
[filter setDefaults];
// 修改亮度 -1---1 数越大越亮
[filter setValue:@(self.light) forKey:@"inputBrightness"];
// 修改饱和度 0---2
[filter setValue:@(self.saturation) forKey:@"inputSaturation"];
// 修改对比度 0---4
[filter setValue:@(self.contrast) forKey:@"inputContrast"];
CIImage *outputImage = [filter outputImage];
现在就剩显示出来了,我们选择了用opengl进行渲染,用CPU的话会稍微有些卡顿。
3.显示
初始化glkview,进行配置。然后我们传入一个经过CIFilter处理的CIImage就可以渲染出来了。
- (void)initGlkView{
EAGLContext *eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
_glkView = [[GLKView alloc] initWithFrame:CGRectZero context:eaglContext];
_glkView.delegate = self;
[_glkView bindDrawable];
_glkView.enableSetNeedsDisplay = NO;
[self addSubview:_glkView];
[EAGLContext setCurrentContext:eaglContext];
_imageContext = [CIContext contextWithEAGLContext:eaglContext options:@{kCIContextWorkingColorSpace:[NSNull null]}];
}
在传入CIImage的方法里,调用display方法进行绘制。
- (void)setRenderImg:(CIImage *)renderImg{
_renderImg = renderImg;
[self.glkView display];
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
[self.imageContext drawImage:self.renderImg inRect:CGRectMake(0, 0, self.glkView.drawableWidth, self.glkView.drawableHeight) fromRect:[self.renderImg extent]];
}