Я пытаюсь использовать Framework Брэда Ларсона в качестве эталона для получения некоторых базовых тестов обработки изображений. До сих пор я реализовал фильтр сепии, и он вроде работает, проблема в том, что я получаю обработанное изображение и необработанное изображение каждый цикл рендеринга (например, если я устанавливаю fps на 1 сек, первая секунда рендеринга, вторая нет, третий да и тд). И я провел часы, и я не могу понять, почему.
У меня есть 2 окна, слой предварительного просмотра видео и glkview, где я рендерил свой буфер.
Это мой код, который представляет собой просто попытку синтезировать ранее упомянутую платформу:
Просмотр сделал Способ загрузки:
- (void)viewDidLoad
{
[super viewDidLoad];
session = [[AVCaptureSession alloc] init];
// Add inputs and outputs.
if ([session canSetSessionPreset:AVCaptureSessionPreset640x480]) {
session.sessionPreset = AVCaptureSessionPreset640x480;
}
else {
// Handle the failure.
NSLog(@"Cannot set session preset to 640x480");
}
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
// Handle the error appropriately.
NSLog(@"Could create input: %@", error);
}
if ([session canAddInput:input]) {
[session addInput:input];
}
else {
// Handle the failure.
NSLog(@"Could not add input");
}
captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
[captureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
[captureVideoPreviewLayer setBounds:videoLayer.layer.bounds];
[captureVideoPreviewLayer setPosition:videoLayer.layer.position];
[videoLayer.layer addSublayer:captureVideoPreviewLayer];
[session startRunning];
// OPENGL stuff
self.imageView.context = [[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context];
if ([self createFilterProgram]) {
NSLog(@"Filter Program Created");
}
if ([self createOutputProgram]) {
NSLog(@"Output Program Created");
}
if (CVOpenGLESTextureCacheCreate != NULL)
{
[GPUImageOpenGLESContext useImageProcessingContext];
CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, (__bridge void *)[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context], NULL, &coreVideoTextureCache);
if (err)
{
NSAssert(NO, @"Error at CVOpenGLESTextureCacheCreate %d");
}
else
NSLog(@"Initial CVOpenGLESTextureCache created");
// Need to remove the initially created texture
if (outputTexture)
{
glDeleteTextures(1, &outputTexture);
outputTexture = 0;
}
}
// DATA OUTPUT
dataOutput = [[AVCaptureVideoDataOutput alloc] init];
if ([session canAddOutput:dataOutput]) {
[session addOutput:dataOutput];
dataOutput.videoSettings =
[NSDictionary dictionaryWithObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA]
forKey: (id)kCVPixelBufferPixelFormatTypeKey];
AVCaptureConnection *connection = [dataOutput connectionWithMediaType:AVMediaTypeVideo];
[connection setVideoMaxFrameDuration:CMTimeMake(1, 1)];
[connection setVideoMinFrameDuration:CMTimeMake(1, 1)];
}
else {
// Handle the failure.
NSLog(@"Could not add output");
}
// DATA OUTPUT END
//dispatch_queue_t queue = dispatch_queue_create("MyQueue", NULL);
[dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
//dispatch_release(queue);
}
и метод делегата из захвата видео:
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
NSLog(@"Render");
//CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
CVImageBufferRef cameraFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
int bufferWidth = CVPixelBufferGetWidth(cameraFrame);
int bufferHeight = CVPixelBufferGetHeight(cameraFrame);
if (CVOpenGLESTextureCacheCreate != NULL)
{
NSLog(@"Not Null");
CVPixelBufferLockBaseAddress(cameraFrame, 0);
[GPUImageOpenGLESContext useImageProcessingContext];
CVOpenGLESTextureRef texture = NULL;
CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, coreVideoTextureCache, cameraFrame, NULL, GL_TEXTURE_2D, GL_RGBA, bufferWidth, bufferHeight, GL_BGRA, GL_UNSIGNED_BYTE, 0, &texture);
if (!texture || err) {
NSLog(@"CVOpenGLESTextureCacheCreateTextureFromImage failed (error: %d)", err);
return;
}
outputTexture = CVOpenGLESTextureGetName(texture);
glBindTexture(CVOpenGLESTextureGetTarget(texture), outputTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// "setInputSize" method
CGSize sizeOfFBO = CGSizeMake(bufferWidth, bufferHeight);
// "newFrameReady" method
static const GLfloat squareVertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
static const GLfloat squareTextureCoordinates[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
[GPUImageOpenGLESContext useImageProcessingContext];
// "setFilterFBO" method
if (!filterFramebuffer)
{
NSLog(@"New Filter Frame buffer");
glActiveTexture(GL_TEXTURE1);
glGenFramebuffers(1, &filterFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, filterFramebuffer);
NSLog(@"Filter size: %f, %f", sizeOfFBO.width, sizeOfFBO.height);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, (int)sizeOfFBO.width, (int)sizeOfFBO.height);
glBindTexture(GL_TEXTURE_2D, outputTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)sizeOfFBO.width, (int)sizeOfFBO.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, outputTexture, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
NSAssert(status == GL_FRAMEBUFFER_COMPLETE, @"Incomplete filter FBO: %d", status);
}
glBindFramebuffer(GL_FRAMEBUFFER, filterFramebuffer);
glViewport(0, 0, (int)sizeOfFBO.width, (int)sizeOfFBO.height);
// END "setFilterFBO" method
glUseProgram(filterProgram); // This Program MUST be the filter one
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, outputTexture);
glUniform1i(uniforms[filterInputTextureUniform], 2);
glVertexAttribPointer(position, 2, GL_FLOAT, 0, 0, squareVertices);
glVertexAttribPointer(inputTextureCoordinate, 2, GL_FLOAT, 0, 0, squareTextureCoordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
static const GLfloat squareVertices2[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
static const GLfloat textureCoordinates2[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
[GPUImageOpenGLESContext useImageProcessingContext];
//[self setDisplayFramebuffer];
if (!displayFramebuffer)
{
NSLog(@"New Display Frame buffer");
glGenFramebuffers(1, &displayFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, displayFramebuffer);
glGenRenderbuffers(1, &displayRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, displayRenderbuffer);
[[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context] renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)imageView.layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
NSLog(@"Backing width: %d, height: %d", backingWidth, backingHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, displayRenderbuffer);
GLuint framebufferCreationStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
NSAssert(framebufferCreationStatus == GL_FRAMEBUFFER_COMPLETE, @"Failure with display framebuffer generation");
}
glBindFramebuffer(GL_FRAMEBUFFER, displayFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
glUseProgram(outputProgram);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, outputTexture);
glUniform1i(uniformsOutput[outputTextureUniform], 4);
glVertexAttribPointer(outputPosition, 2, GL_FLOAT, 0, 0, squareVertices2);
glVertexAttribPointer(outputTextureCoordinate, 2, GL_FLOAT, 0, 0, textureCoordinates2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindRenderbuffer(GL_RENDERBUFFER, displayRenderbuffer);
[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] presentBufferForDisplay];
//glBindRenderbuffer(GL_RENDERBUFFER, 0);
CVPixelBufferUnlockBaseAddress(cameraFrame, 0);
glBindTexture(outputTexture, 0);
// Flush the CVOpenGLESTexture cache and release the texture
CVOpenGLESTextureCacheFlush(coreVideoTextureCache, 0);
CFRelease(texture);
outputTexture = 0;
}
else
NSLog(@"No support for fast CVOpenGLESTextureCacheCreate");
}
Это оригинальная структура Брэда (что довольно круто) http://www.sunsetlakesoftware.com/2012/02/12/introducing-gpuimage-framework
Если кто-нибудь знает, почему это может происходить, я был бы признателен за помощь.
До сих пор я думал, что это может быть вызвано следующими причинами:
- Рендеринг 2 раза в течение цикла (который я отбросил, потому что это
происходит каждые 1 секунду, и даже если это произошло очень быстро в последний раз
останется обработанный)
- Включение или отключение состояния OpenGL (которое, я думаю, может быть
причина, но не могу найти где)
- Неправильное использование буферов (я использую ту же самую переменную буфера
для обоих шагов это может вызвать проблемы)
- Это может иметь отношение к контексту (я считаю, что контекст
где все рисуется, так что, если я работаю над glkview
контекст, я мог бы перезаписать изображение)
Пожалуйста, исправьте меня, если некоторые из этих идей неверны, это моя первая попытка обработки с использованием OpenGL
Спасибо!
EDIT:
Как предложил Брэд, я использовал профилировщик OpenGL и получил следующие результаты:
Похоже, этот инструмент отлично подходит для оптимизации кода OpenGL.