IOS:Camera的特性分析与使用3_OPENGL特效

本来想用2个篇幅结束Camera软件部分的介绍,后来发现,非常重要的一点OpenGL还没介绍,所以又增加了这一篇。

这篇主要描述一下几个方面的内容:

(1)录像界面OPENGL展示

(2)录像实时特效处理

(3)视频等比例缩放、旋转 如:等比例、16:9 4:3 1:1等

这个部分我思来想去缺失不太好讲,设计到的知识太多,尤其是OpenGL的一些专业知识,通过一篇博客普及OpenGL的知识显然不科学,所以只能了解一个流程,至于里面到底是怎么回事,请大家找本OpenGL的书看看,我想等这几个博客完工之后,也写几篇OpenGL的博客呵呵。

我们的整个流程是,首先从AVCaptureSession拿到视频拍摄时候的数据流,然后特效处理(特效这块可以参考另一个Image&Animation专栏),然后初始化OpenGL开始进行纹理贴图。

(1)如何拿到视频数据流?

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection

{

    if ( videooutput == captureOutput ) {

        OSStatus err = CMBufferQueueEnqueue(previewBufferQueue, sampleBuffer);

        if ( !err ) {

            dispatch_async(dispatch_get_main_queue(), ^{

                CMSampleBufferRef sbuf = (CMSampleBufferRef)CMBufferQueueDequeueAndRetain(previewBufferQueue);

                if (sbuf) {

                    CVImageBufferRef pixBuf = CMSampleBufferGetImageBuffer(sbuf);

                    if (effectflag) {

                        特效处理

                    }

                        OpenGL纹理展示

                    CFRelease(sbuf);

                }

            });

        }

    }

}

AVCaptureSession初始化完成之后我们可以设置一个回调方法,在这个回调方法中,可以很方便的拿到我们需要处理的图像数据。

(2)如何进行图片的特效处理

这又是一个非常复杂的内容,我也专门为此写了另外一篇博客:

这中间牵扯到各种图像处理算法,neon、汇编优化,ARM内部寄存器的使用等等。

这里我们只说如何吧ImageBuffer转化为RGBA像素:

unsigned char *pixel = (unsigned char *)CVPixelBufferGetBaseAddress(pixelBuffer);

这里pixel存放的就是图片的RGBA像素值。

(3)OpenGL纹理贴图

3.1//在使用Opengles的时候需要重构CAEAGLLayer图层

+ (Class)layerClass

{

    return [CAEAGLLayer class];

}

3.2//      设置CAEAGLLayer

        CAEAGLLayer*eaglLayer = (CAEAGLLayer *)self.layer;

3.3设置CAEAGLLayer层的属性RGBA8

3.4//      使用opengl2.0 创建图像绘制上下文

        oglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

3.5//  设置oglContext为当前上下文

3.2 //   glEnable(GL_DEPTH_TEST)用来开启更新深度缓冲区的功能

3.3 //  创建帧缓冲区

3.4 //  讲帧缓冲区绑定在绘图管线上

3.5 //  创建绘图缓冲区

3.6 //  讲绘图缓冲区绑定在管线上

3.7 //  为绘图缓冲区(或者叫渲染缓冲区分配空间)

3.8 //  获取当前绘图缓冲区(渲染缓冲区的)宽和高

3.9 //  讲渲染缓冲区与帧缓冲区绑定在一起

3.10 //  检查当前帧缓冲区的状态是否有效

3.11 //  创建一个opengl的纹理对象

3.12 //  加载定点和片段着色器

3.13 //  创建并初始化这个工程对象

对应代码如下:

//在使用Opengles的时候需要重构CAEAGLLayer图层

+ (Class)layerClass

{

    return [CAEAGLLayer class];

}

- (BOOL)initializeBuffers

{

    //  设置oglContext为当前上下文

    if ([EAGLContext currentContext] != oglContext) {

        if ([EAGLContext setCurrentContext:oglContext]) {

            NSLog(@"setCurrentContext error... ...");

        }

    }

    

BOOL success = YES;

    //  设置图层的framebounds

    CGRect rtFullscreem = [[UIScreen mainScreen] bounds];

    CGRect rtCurrframe = self.layer.frame;

    CGRect rtCurrbounds = self.layer.bounds;

    self.layer.frame = rtFullscreem;

    self.layer.bounds = rtFullscreem;

    

NSLog(@"size{%f %f %f %f}", rtFullscreem.origin.x, rtFullscreem.origin.x, rtFullscreem.size.width, rtFullscreem.size.height);

    

    //    glEnable(GL_DEPTH_TEST) 用来开启更新深度缓冲区的功能,也就是,如果通过比较后深度值发生变化了,会进行更新深度缓冲区的操作。启动它,OpenGL就可以跟踪再Z轴上的像素,这样,它只会再那个像素前方没有东西时,才会绘画这个像素。

    //  一般这个功能开启之后绘制3D效果比较好

glDisable(GL_DEPTH_TEST);

    //  创建帧缓冲区

    glGenFramebuffers(1, &frameBufferHandle);

    //  讲帧缓冲区绑定在绘图管线上

    glBindFramebuffer(GL_FRAMEBUFFER, frameBufferHandle);

    //  创建绘图缓冲区

    glGenRenderbuffers(1, &colorBufferHandle);

    //  讲绘图缓冲区绑定在管线上

    glBindRenderbuffer(GL_RENDERBUFFER, colorBufferHandle);

    

    //  为绘图缓冲区(或者叫渲染缓冲区分配空间)

    [oglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];

    

    //  获取当前绘图缓冲区(渲染缓冲区的)宽和高

glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &renderBufferWidth);

    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &renderBufferHeight);

    //  讲渲染缓冲区与帧缓冲区绑定在一起

    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle);

    //  检查当前帧缓冲区的状态是否有效

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {

        NSLog(@"Failure with framebuffer generation 0x%X", glCheckFramebufferStatus(GL_FRAMEBUFFER));

success = NO;

}

    

    //  Create a new CVOpenGLESTexture cache

    //  创建一个opengl的纹理对象

//  oglContext 中创建纹理对象

    CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, oglContext, NULL, &videoTextureCache);

    if (err) {

        NSLog(@"Error at CVOpenGLESTextureCacheCreate %d", err);

        success = NO;

    }

    

    // Load vertex and fragment shaders

    //  加载定点和片段着色器

    const GLchar *vertSrc = str_passThrough_v;//[self readFile:@"passThrough.vsh"];

    const GLchar *fragSrc = str_passThrough_f;//  [self readFile:@"passThrough.fsh"];

    

    // attributes

    GLint attribLocation[NUM_ATTRIBUTES] = {

        ATTRIB_VERTEX, ATTRIB_TEXTUREPOSITON,

    };

    GLchar *attribName[NUM_ATTRIBUTES] = {

        "position", "textureCoordinate",

    };

    //  创建并初始化这个工程对象

    glueCreateProgram(vertSrc, fragSrc,

                      NUM_ATTRIBUTES, (const GLchar **)&attribName[0], attribLocation,

                      0, 0, 0, // we don't need to get uniform locations in this example

                      &passThroughProgram);

    

    if (!passThroughProgram)

        success = NO;

    

    self.layer.frame = rtCurrframe;

    self.layer.bounds = rtCurrbounds;

    return success;

}

最后我们再来看看如何对所播放的视频屏幕进行等比例缩放,16:9等缩放

这里我们首先需要设置一个属性:

glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);

textureVertices 是一个数组,用于进行纹理贴图时画面设置:

全屏幕播放

GLfloat squareVertices0[8] = {

        -1.0f, -1.0f,

        1.0f, -1.0f,

        -1.0f1.0f,

        1.0f1.0f

};

等比例拉伸

    GLfloat squareVertices1[8] = {

        -0.5625f, -1.0f,

        0.5625f, -1.0f,

        -0.5625f1.0f,

        0.5625f1.0f

};

这个数据是啥意思呢?看下面两个图


屏幕拍摄为1920*1080,所以1080/1920=0.5625.注意拍摄时候 宽高倒置。

猜你喜欢

转载自blog.csdn.net/u014011807/article/details/46911011
今日推荐