I am trying to use a combination of GPUImage and AVVideoCompositing to implement a live color key filter between two videos. Doing this naively using CIImage imageFromCVPixelBuffer in CGImage in GPUImage from CGImage to CIImage to CVPixelBuffer extremely inefficient and leads to memory problems.
I noticed that there are texture objects, rendering objects, and frame buffers in the GPUImage Framework. I was hoping I could use CVOpenGLESTextureCacheCreateTextureFromImage on iOS to save everything on the GPU.
I donβt think I fully understand the internal workings of the framework because I suggested that I could create a filter chain for the GPUImageTextureInput object and then get the renderTarget filter, which is CVPixelBufferRef . renderTarget below is always nil, and calling imageFromCurrentFrameBuffer will give me a gray square, which is not my image.
Please note that the example below is not a color key, but a simple brightness filter on a single video to try to prove the concept.
@implementation MyCustomCompositor : NSObject <AVVideoCompositing> - (instancetype)init { self = [super init]; if (self) { CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, [GPUImageContext sharedImageProcessingContext].context, NULL, &_textureCache); } return self; } - (NSDictionary<NSString *,id> *)requiredPixelBufferAttributesForRenderContext { return @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @[@(kCVPixelFormatType_32BGRA)], (NSString *)kCVPixelBufferOpenGLCompatibilityKey : @YES}; } - (NSDictionary<NSString *,id> *)sourcePixelBufferAttributes { return @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @[@(kCVPixelFormatType_32BGRA)], (NSString *)kCVPixelBufferOpenGLCompatibilityKey : @YES}; } - (void)startVideoCompositionRequest:(AVAsynchronousVideoCompositionRequest *)asyncVideoCompositionRequest { @autoreleasepool { CVPixelBufferRef mePixelBuffer = [asyncVideoCompositionRequest sourceFrameByTrackID:200]; CVPixelBufferLockBaseAddress(mePixelBuffer, kCVPixelBufferLock_ReadOnly); CVOpenGLESTextureRef meTextureRef = NULL; size_t width = CVPixelBufferGetWidth(mePixelBuffer); size_t height = CVPixelBufferGetHeight(mePixelBuffer); CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _textureCache, mePixelBuffer, NULL, GL_TEXTURE_2D, GL_BGRA, (int)width, (int)height, GL_BGRA, GL_UNSIGNED_BYTE, 0, &meTextureRef); GPUImageTextureInput *meTextureInput = [[GPUImageTextureInput alloc] initWithTexture:CVOpenGLESTextureGetName(meTextureRef) size:CGSizeMake(width, height)]; GPUImageBrightnessFilter *filter = [[GPUImageBrightnessFilter alloc] init]; filter.brightness = 0.5; [meTextureInput addTarget:filter]; [filter setFrameProcessingCompletionBlock:^(GPUImageOutput *imageOutput, CMTime time) { [asyncVideoCompositionRequest finishWithComposedVideoFrame:((GPUImageBrightnessFilter *)imageOutput).renderTarget]; }]; [meTextureInput processTextureWithFrameTime:kCMTimeZero]; CFRelease(meTextureRef); CVOpenGLESTextureCacheFlush(_textureCache, 0); CVPixelBufferUnlockBaseAddress(mePixelBuffer, kCVPixelBufferLock_ReadOnly); } }
I do not use GPUMovieWriter or the video API in GPUImage because I need to control my composition in more detail. The composition may consist of several instructions with color keys that refer to different green overlays of the video in different time ranges, and it seems to me that the movie APIs in GPUImage limited to filtering the entire video file. I also need compositional abilities to control tracks and audio mixes.
I tried wrapping my head doing all this in GL with custom shaders, but I decided that I would use existing frameworks that seem to do what I'm trying to do.