ios opengl 播放 yuv数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37677536/article/details/78782501

android opengl 播放 yuv数据:
http://blog.csdn.net/m0_37677536/article/details/78783267
yuv格式数据是怎么来的呢:
Kr = 0.2126
Kb = 0.0722
从 RGB 到 YUV 转换的定义以下列内容开始:L = Kr * R + Kb * B + (1 – Kr – Kb) * G然后,按照下列方式获得 YUV 值:
Y = floor(2^(M-8) * (219*(L–Z)/S + 16) + 0.5)
U = clip3(0, 2^M-1, floor(2^(M-8) * (112*(B-L) / ((1-Kb)*S) + 128) + 0.5))
V = clip3(0, 2^M-1, floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5))
就是这么定义计算出来的值,那么要还原为rgb也是通过计算得到,是一种定好的标准,大家不要纠结为什么这么算。
现在我们要说的怎么用显卡给把yuv绘制出来。
要绘制yuv需要opengl的可编程管线技术,也就是把yuv数据传到gpu通过gpu计算得到RGB再绘制出来。计算方法需要我们编写,计算的代码也需要我们传到gpu上去运行。计算的方法就是shader里面的内容。shader就是要在gpu上运行的程序
总之需要这么几个步骤:编写shader->编译shader->链成gpu程序(代码中的program)->分别创建yuv纹理对象->找到yuv纹理对象对应的显卡插槽(也就是要给gpu中运行的纹理对象传数据的地址)->给yuv纹理对象绑定数据->绘图。
下面代码中:
setupYUVTexture 这个方法就是创建纹理对象
loadShader 这个方法就是创建一个gpu程序
更细节的里面注释挺详细的,总体安装上面的步骤去理解就容易懂了。
有完整的播放器,并且有完整源码下载ffmpeg+opengl+openal:
http://blog.csdn.net/m0_37677536/article/details/78769362
opengl代码:
glView.h

#import <UIKit/UIKit.h>

#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>
#import <OpenGLES/EAGL.h>
#include <sys/time.h>
#import "YUV_GL_DATA.h"

@interface OpenglView : UIView
{
    /**
     OpenGL绘图上下文
     */
    EAGLContext             *_glContext;

    /**
     帧缓冲区
     */
    GLuint                  _framebuffer;

    /**
     渲染缓冲区
     */
    GLuint                  _renderBuffer;

    /**
     着色器句柄
     */
    GLuint                  _program;

    /**
     YUV纹理数组
     */
    GLuint                  _textureYUV[3];

    /**
     视频宽度
     */
    GLuint                  _videoW;

    /**
     视频高度
     */
    GLuint                  _videoH;

    GLsizei                 _viewScale;

    //void                    *_pYuvData;

#ifdef DEBUG
    struct timeval      _time;
    NSInteger           _frameRate;
#endif
}
#pragma mark - 接口
- (void)displayYUV420pData:(H264YUV_Frame *) frame;
- (void)setVideoSize:(GLuint)width height:(GLuint)height;
/** 
 清除画面
 */
- (void)clearFrame;

@end

glView.m

//
//  OpenglView.m
//  FFmpeg-project
//
//  Created by huizai on 2017/9/22.
//  Copyright © 2017年 huizai. All rights reserved.
//

#import "OpenglView.h"

enum AttribEnum
{
    ATTRIB_VERTEX,
    ATTRIB_TEXTURE,
    ATTRIB_COLOR,
};

//YUV数据枚举
enum TextureType
{
    TEXY = 0,
    TEXU,
    TEXV,
    TEXC
};

@implementation OpenglView

#pragma mark -  初始化等操作
- (BOOL)doInit{
    //用来显示opengl的图形
    CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;
    //eaglLayer.opaque = YES;

    //设为不透明
    eaglLayer.opaque = YES;

    eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                    [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
                                    kEAGLColorFormatRGB565, kEAGLDrawablePropertyColorFormat,
                                    //[NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking,
                                    nil];

    //设置分辨率
    self.contentScaleFactor = [UIScreen mainScreen].scale;
    _viewScale = [UIScreen mainScreen].scale;

    //创建上下文
    _glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

    //[self debugGlError];

    //上下文创建失败则直接返回no
    if(!_glContext || ![EAGLContext setCurrentContext:_glContext])
    {
        return NO;
    }

    //创建纹理
    [self setupYUVTexture];

    //加载着色器
    [self loadShader];

    //像素数据对齐,第二个参数默认为4,一般为1或4
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

    //使用着色器
    glUseProgram(_program);

    //获取一致变量的存储位置
    GLuint textureUniformY = glGetUniformLocation(_program, "SamplerY");
    GLuint textureUniformU = glGetUniformLocation(_program, "SamplerU");
    GLuint textureUniformV = glGetUniformLocation(_program, "SamplerV");

    //对几个纹理采样器变量进行设置
    glUniform1i(textureUniformY, 0);
    glUniform1i(textureUniformU, 1);
    glUniform1i(textureUniformV, 2);

    return YES;
}


-(instancetype)initWithFrame:(CGRect)frame{

    self = [super initWithFrame:frame];

    if (self) {

        //没有初始化成功
        if (![self doInit]) {

            self = nil;
        }
    }

    return self;
}

-(instancetype)initWithCoder:(NSCoder *)aDecoder{

    self = [super initWithCoder:aDecoder];

    if (self) {

        //没有初始化成功
        if (![self doInit]) {

            self = nil;
        }
    }

    return self;
}

-(void)layoutSubviews{

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        //互斥锁
        @synchronized (self) {

            [EAGLContext setCurrentContext:_glContext];

            //清除缓冲区
            [self destoryFrameAndRenderBuffer];

            //创建缓冲区
            [self createFrameAndRenderBuffer];

        }

        //把数据显示在这个视窗上
        glViewport(1, 1, self.bounds.size.width*_viewScale - 2, self.bounds.size.height*_viewScale - 2);
    });
}

#pragma mark -  设置opengl

/**
 不写的话,设置描绘属性会崩溃

 @return <#return value description#>
 */
+ (Class)layerClass
{
    return [CAEAGLLayer class];
}

/**
 创建缓冲区

 @return <#return value description#>
 */
- (BOOL)createFrameAndRenderBuffer
{
    //创建帧缓冲绑定
    glGenFramebuffers(1, &_framebuffer);
    //创建渲染缓冲
    glGenRenderbuffers(1, &_renderBuffer);

    //将之前用glGenFramebuffers创建的帧缓冲绑定为当前的Framebuffer(绑定到context上?).
    glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
    //Renderbuffer绑定到context上,此时当前Framebuffer完全由renderbuffer控制
    glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);

    //分配空间
    if (![_glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer])
    {
        NSLog(@"attach渲染缓冲区失败");
    }

    //这个函数看起来有点复杂,但其实它很好理解的。它要做的全部工作就是把把前面我们生成的深度缓存对像与当前的FBO对像进行绑定,当然我们要注意一个FBO有多个不同绑定点,这里是要绑定在FBO的深度缓冲绑定点上。
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);

    //检查当前帧缓存的关联图像和帧缓存参数
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        NSLog(@"创建缓冲区错误 0x%x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
        return NO;
    }
    return YES;
}

/**
 清除缓冲区
 */
- (void)destoryFrameAndRenderBuffer
{
    if (_framebuffer)
    {
        //删除FBO
        glDeleteFramebuffers(1, &_framebuffer);
    }

    if (_renderBuffer)
    {
        //删除渲染缓冲区
        glDeleteRenderbuffers(1, &_renderBuffer);
    }

    _framebuffer = 0;
    _renderBuffer = 0;
}
/**
 创建纹理
 */
- (void)setupYUVTexture{

    if (_textureYUV[TEXY])
    {
        //删除纹理
        glDeleteTextures(3, _textureYUV);
    }

    //生成纹理
    glGenTextures(3, _textureYUV);
    if (!_textureYUV[TEXY] || !_textureYUV[TEXU] || !_textureYUV[TEXV])
    {
        NSLog(@"<<<<<<<<<<<<纹理创建失败!>>>>>>>>>>>>");
        return;
    }

    //选择当前活跃单元
    glActiveTexture(GL_TEXTURE0);
    //绑定Y纹理
    glBindTexture(GL_TEXTURE_2D, _textureYUV[TEXY]);
    //纹理过滤函数
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//放大过滤
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_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);//垂直方向

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, _textureYUV[TEXU]);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_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);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, _textureYUV[TEXV]);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_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);

}

#define FSH @"varying lowp vec2 TexCoordOut;\
\
uniform sampler2D SamplerY;\
uniform sampler2D SamplerU;\
uniform sampler2D SamplerV;\
\
void main(void)\
{\
mediump vec3 yuv;\
lowp vec3 rgb;\
\
yuv.x = texture2D(SamplerY, TexCoordOut).r;\
yuv.y = texture2D(SamplerU, TexCoordOut).r - 0.5;\
yuv.z = texture2D(SamplerV, TexCoordOut).r - 0.5;\
\
rgb = mat3( 1,       1,         1,\
0,       -0.39465,  2.03211,\
1.13983, -0.58060,  0) * yuv;\
\
gl_FragColor = vec4(rgb, 1);\
\
}"

#define VSH @"attribute vec4 position;\
attribute vec2 TexCoordIn;\
varying vec2 TexCoordOut;\
\
void main(void)\
{\
gl_Position = position;\
TexCoordOut = TexCoordIn;\
}"

/**
 加载着色器
 */
- (void)loadShader{
    /**
     1 编译着色
     */
    GLuint vertexShader = [self compileShader:VSH withType:GL_VERTEX_SHADER];
    GLuint fragmentShader = [self compileShader:FSH withType:GL_FRAGMENT_SHADER];

    /**
     2
     */
    //创建程序容器
    _program = glCreateProgram();
    //绑定shader到program
    glAttachShader(_program, vertexShader);
    glAttachShader(_program, fragmentShader);

    /**
     绑定需要在link之前
     */
    //把顶点属性索引绑定到顶点属性名
    glBindAttribLocation(_program, ATTRIB_VERTEX, "position");
    glBindAttribLocation(_program, ATTRIB_TEXTURE, "TexCoordIn");

    //链接
    glLinkProgram(_program);

    /**
     3
     */
    GLint linkSuccess;
    //查询相关信息,并将数据返回到linkSuccess
    glGetProgramiv(_program, GL_LINK_STATUS, &linkSuccess);
    if (linkSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetProgramInfoLog(_program, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"<<<<着色器连接失败 %@>>>", messageString);
        //exit(1);
    }

    if (vertexShader)
    /*
     释放内存不能立刻删除
     If a shader object to be deleted is attached to a program object, it will be flagged for deletion, but it will not be deleted until it is no longer attached to any program object…
     shader 删除该着色器对象(如果一个着色器对象在删除前已经链接到程序对象中,那么当执行glDeleteShader函数时不会立即被删除,而是该着色器对象将被标记为删除,器内存被释放一次,它不再链接到其他任何程序对象)
     */
        glDeleteShader(vertexShader);
    if (fragmentShader)
        glDeleteShader(fragmentShader);
}

/**
 编译着色代码,使用着色器

 @param shaderString 代码
 @param shaderType   类型

 @return 成功返回着色器,失败返回-1
 */
- (GLuint)compileShader:(NSString*)shaderString withType:(GLenum)shaderType
{

    /**
     1
     */
    if (!shaderString) {
        //        NSLog(@"Error loading shader: %@", error.localizedDescription);
        exit(1);
    }
    else
    {
        //NSLog(@"shader code-->%@", shaderString);
    }

    /**
     2 分别创建一个顶点着色器对象和一个片段着色器对象
     */
    GLuint shaderHandle = glCreateShader(shaderType);

    /**
     3 分别将顶点着色程序的源代码字符数组绑定到顶点着色器对象,将片段着色程序的源代码字符数组绑定到片段着色器对象
     */
    const char * shaderStringUTF8 = [shaderString UTF8String];
    int shaderStringLength = (int)[shaderString length];
    glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);

    /**
     4 分别编译顶点着色器对象和片段着色器对象
     */
    glCompileShader(shaderHandle);

    /**
     5
     */
    GLint compileSuccess;

    //获取编译情况
    glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
    if (compileSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        exit(1);
    }

    return shaderHandle;
}

#pragma mark -  接口



/**
 设置大小

 @param width  界面宽
 @param height 界面高
 */
-(void)setVideoSize:(GLuint)width height:(GLuint)height{

    //给宽高赋值
    _videoH = height;
    _videoW = width;

    //开辟内存空间
    //为什么乘1.5而不是1: width * hight =Y(总和) U = Y / 4   V = Y / 4
    void *blackData = malloc(width * height * 1.5);

    if (blackData) {

        /**
         对内存空间清零,作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法

         @param __b#>   源数据 description#>
         @param __c#>   填充数据 description#>
         @param __len#> 长度 description#>

         @return <#return value description#>
         */
        memset(blackData, 0x0, width * height * 1.5);
    }
    /*
     Apple平台不允许直接对Surface进行操作.这也就意味着在Apple中,不能通过调用eglSwapBuffers函数来直接实现渲染结果在目标surface上的更新.
     在Apple平台中,首先要创建一个EAGLContext对象来替代EGLContext (不能通过eglCreateContext来生成), EAGLContext的作用与EGLContext是类似的.
     然后,再创建相应的Framebuffer和Renderbuffer.
     Framebuffer象一个Renderbuffer集(它可以包含多个Renderbuffer对象).

     Renderbuffer有三种:  color Renderbuffer, depth Renderbuffer, stencil Renderbuffer.

     渲染结果是先输出到Framebuffer中,然后通过调用context的presentRenderbuffer,将Framebuffer上的内容提交给之前的CustumView.
     */

    //设置当前上下文
    [EAGLContext setCurrentContext:_glContext];


    /*
     target —— 纹理被绑定的目标,它只能取值GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D或者GL_TEXTURE_CUBE_MAP;
     texture —— 纹理的名称,并且,该纹理的名称在当前的应用中不能被再次使用。
     glBindTexture可以让你创建或使用一个已命名的纹理,调用glBindTexture方法,将target设置为GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D或者GL_TEXTURE_CUBE_MAP,并将texture设置为你想要绑定的新纹理的名称,即可将纹理名绑定至当前活动纹理单元目标。当一个纹理与目标绑定时,该目标之前的绑定关系将自动被打破。纹理的名称是一个无符号的整数。在每个纹理目标中,0被保留用以代表默认纹理。纹理名称与相应的纹理内容位于当前GL rendering上下文的共享对象空间中。
     */

    //绑定Y纹理
    glBindTexture(GL_TEXTURE_2D, _textureYUV[TEXY]);


    /**
     根据像素数据,加载纹理

     @param target#>         指定目标纹理,这个值必须是GL_TEXTURE_2D。 description#>
     @param level#>          执行细节级别。0是最基本的图像级别,n表示第N级贴图细化级别 description#>
     @param internalformat#> 指定纹理中的颜色格式。可选的值有GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE, GL_LUMINANCE_ALPHA 等几种。 description#>
     @param width#>          纹理的宽度 description#>
     @param height#>         高度 description#>
     @param border#>         纹理的边框宽度,必须为0 description#>
     @param format#>         像素数据的颜色格式, 不需要和internalformatt取值必须相同。可选的值参考internalformat。 description#>
     @param type#>           指定像素数据的数据类型。可以使用的值有GL_UNSIGNED_BYTE,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4,GL_UNSIGNED_SHORT_5_5_5_1等。 description#>
     @param pixels#>         指定内存中指向图像数据的指针 description#>

     @return <#return value description#>
     */
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED_EXT, width, height, 0, GL_RED_EXT, GL_UNSIGNED_BYTE, blackData);

    //绑定U纹理
    glBindTexture(GL_TEXTURE_2D, _textureYUV[TEXU]);
    //加载纹理
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED_EXT, width/2, height/2, 0, GL_RED_EXT, GL_UNSIGNED_BYTE, blackData + width * height);

    //绑定V数据
    glBindTexture(GL_TEXTURE_2D, _textureYUV[TEXV]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED_EXT, width/2, height/2, 0, GL_RED_EXT, GL_UNSIGNED_BYTE, blackData + width * height * 5 / 4);

    //释放malloc分配的内存空间
    free(blackData);
}


/**
 清除画面
 */
-(void)clearFrame{

    if ([self window])
    {
        [EAGLContext setCurrentContext:_glContext];
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
        [_glContext presentRenderbuffer:GL_RENDERBUFFER];
    }
}


/**
 显示YUV数据
 */
- (void)displayYUV420pData:(H264YUV_Frame *) frame{

    if (!self.window) {
        return;
    }
    int w = frame->width;
    int h = frame->height;
    //加互斥锁,防止其他线程访问
    @synchronized (self) {

        if (w != _videoW || h != _videoH) {
            [self setVideoSize:(GLuint)w height:(GLuint)h];
        }

        //设置当前上下文
        [EAGLContext setCurrentContext:_glContext];

        //绑定
        glBindTexture(GL_TEXTURE_2D, _textureYUV[TEXY]);

        /**
         更新纹理
         @param target#>  指定目标纹理,这个值必须是GL_TEXTURE_2D。 description#>
         @param level#>   执行细节级别。0是最基本的图像级别,n表示第N级贴图细化级别 description#>
         @param xoffset#> 纹理数据的偏移x值 description#>
         @param yoffset#> 纹理数据的偏移y值 description#>
         @param width#>   更新到现在的纹理中的纹理数据的规格宽 description#>
         @param height#>  高 description#>
         @param format#>  像素数据的颜色格式, 不需要和internalformatt取值必须相同。可选的值参考internalformat。 description#>
         @param type#>    颜色分量的数据类型 description#>
         @param pixels#>  指定内存中指向图像数据的指针 description#>

         @return <#return value description#>
         */
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)w, (GLsizei)h, GL_RED_EXT, GL_UNSIGNED_BYTE, frame->luma.dataBuffer);

        glBindTexture(GL_TEXTURE_2D, _textureYUV[TEXU]);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)w/2, (GLsizei)h/2, GL_RED_EXT, GL_UNSIGNED_BYTE, frame->chromaB.dataBuffer);

        glBindTexture(GL_TEXTURE_2D, _textureYUV[TEXV]);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)w/2, (GLsizei)h/2, GL_RED_EXT, GL_UNSIGNED_BYTE, frame->chromaR.dataBuffer);
        //渲染
        [self render];
    }

#ifdef DEBUG

    GLenum err = glGetError();
    if (err != GL_NO_ERROR)
    {
        printf("GL_ERROR=======>%d\n", err);
    }
    struct timeval nowtime;
    gettimeofday(&nowtime, NULL);
    if (nowtime.tv_sec != _time.tv_sec)
    {
        printf("视频 %ld 帧率:   %ld\n", self.tag, (long)_frameRate);
        memcpy(&_time, &nowtime, sizeof(struct timeval));
        _frameRate = 1;
    }
    else
    {
        _frameRate++;
    }
#endif
}


/**
 渲染
 */
-(void)render{

    //设置上下文
    [EAGLContext setCurrentContext:_glContext];

    CGSize size = self.bounds.size;

    //把数据显示在这个视窗上
    glViewport(1, 1, size.width * _viewScale -2, size.height * _viewScale -2);

    /*
     我们如果选定(0, 0), (0, 1), (1, 0), (1, 1)四个纹理坐标的点对纹理图像映射的话,就是映射的整个纹理图片。如果我们选择(0, 0), (0, 1), (0.5, 0), (0.5, 1) 四个纹理坐标的点对纹理图像映射的话,就是映射左半边的纹理图片(相当于右半边图片不要了),相当于取了一张320x480的图片。但是有一点需要注意,映射的纹理图片不一定是“矩形”的。实际上可以指定任意形状的纹理坐标进行映射。下面这张图就是映射了一个梯形的纹理到目标物体表面。这也是纹理(Texture)比上一篇文章中记录的表面(Surface)更加灵活的地方。
     */
    static const GLfloat squareVertices[] = {
        -1.0f, -1.0f,
        1.0f, -1.0f,
        -1.0f,  1.0f,
        1.0f,  1.0f,
    };


    static const GLfloat coordVertices[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f,  0.0f,
        1.0f,  0.0f,
    };

    //更新属性值
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
    //开启定点属性数组
    glEnableVertexAttribArray(ATTRIB_VERTEX);


    glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, coordVertices);
    glEnableVertexAttribArray(ATTRIB_TEXTURE);

    //绘制

    //当采用顶点数组方式绘制图形时,使用该函数。该函数根据顶点数组中的坐标数据和指定的模式,进行绘制。
    //绘制方式,从数组的哪一个点开始绘制(一般为0),顶点个数
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    //将该渲染缓冲区对象绑定到管线上
    glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);

    //把缓冲区(render buffer和color buffer)的颜色呈现到UIView上
    [_glContext presentRenderbuffer:GL_RENDERBUFFER];

}

- (void)dealloc{

    NSLog(@"gl dealloc");
    [self clearFrame];
    if (_glContext) {
       // _glContext = nil;
    }
    [self destoryFrameAndRenderBuffer];
}

@end

数据结构体.h

#ifndef YUV_GL_DATA_h
#define YUV_GL_DATA_h

#pragma pack(push, 1)

typedef struct H264FrameDef
{
    unsigned int    length;
    unsigned char*  dataBuffer;

}H264Frame;

typedef struct  H264YUVDef
{
    unsigned int    width;
    unsigned int    height;
    H264Frame       luma;
    H264Frame       chromaB;
    H264Frame       chromaR;

}H264YUV_Frame;


#pragma pack(pop)

#endif /* YUV_GL_DATA_h */

猜你喜欢

转载自blog.csdn.net/m0_37677536/article/details/78782501