• 实战OpenGLES--iOS平台使用OpenGLES渲染YUV图片


      上一篇文章 实战FFmpeg--iOS平台使用FFmpeg将视频文件转换为YUV文件 演示了如何将视频文件转换为yuv文件保存,现在要做的是如何将yuv文件利用OpenGLES渲染展示出图像画面。要将一个视频文件渲染成连续的视频画面,首先要解决如何渲染一张yuv图片文件。下面就来看看如何通过OpenGLES来渲染yuv图片。

      本文的实现是参照网上的一些零碎的信息做出来的,费了不少精力。使用opengles首先要知道它的基本使用流程,opengles的基本使用参看文章  [OpenGL ES 01]OpenGL ES之初体验 [OpenGL ES 02]OpenGL ES渲染管线与着色器 ,仔细学习这2篇文章就能对opengles的使用会有清楚的认识。至于利用opengles来渲染yuv,这就是深层次的运用了,关于yuv的渲染,雷神的文章那必须要仔细仔细再仔细的看的 最简单的视音频播放示例6:OpenGL播放YUV420P(通过Texture,使用Shader)出,将opengl渲染yuv的流程和细节讲的很透彻,前提是要大概懂点c++方面的东西,这个使用的是vs的开发环境。我对opengles的认识,要得益于这三篇,让我对opengles有了比较深刻的认识。

      现在开始在Xcode中实现yuv文件的渲染。首先,要提一提cocoaChina上的一篇帖子 OpenGL播放yuv视频 ,我写的工程就是参考了这篇帖子中的代码,展示yuv的openglesview的原型就是这篇帖子给的代码,所以这篇帖子以及帖子后面别人的跟帖是需要仔细看的,因为楼主没有给出完整的demo,所以我在仔细研究了这篇帖子之后,写出了实现opengles渲染yuv的demo。

      以下罗列下cocoaChina上的帖子 OpenGL播放yuv视频 渲染yuv中楼主与跟贴者互动中说明的要注意的地方:

    //初始化
    OpenGLView20 *glView = [[OpenGLView20 alloc] initWithFrame:frame];
    //设置视频原始尺寸
    [glView setVideoSize:352 height:288];
    //渲染yuv
    [glView displayYUV420pData:yuvBuffer 352height:288;
    //将352,288换成你自己的
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        NSData *reader = [NSData dataWithContentsOfFile:@"/Users/sky/Desktop/yuvtest/yuvtest/201461110423.yuv"];
        NSLog(@"the reader length is %i", reader.length);
        
        UInt8 * pFrameRGB = (UInt8*)[reader bytes];
        
        //[self.opengl displayYUV420pData:pFrameRGB 1280 height:720];
        
        OpenGLView20 * myview = [[OpenGLView20 alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
        
        [self.view addSubview:myview];
        NSLog(@"window before added %@", myview.window);
        [myview setVideoSize:100 height:50];
        [myview displayYUV420pData:pFrameRGB 100 height:100];
    }
    
    
    我这样写,yuv的图像总是显示不出来,现在出现的问题是在文件的这个地方
    
    #ifdef DEBUG
        
        GLenum err = glGetError();
        if (err != GL_NO_ERROR)
        {
            printf("GL_ERROR=======>%d
    ", err);
        }
        struct timeval nowtime;
        gettimeofday(&nowtime, NULL);
        if (nowtime.tv_sec != _time.tv_sec)
        {
            printf("视频 %d 帧率:   %d
    ", self.tag, _frameRate);
            memcpy(&_time, &nowtime, sizeof(struct timeval));
            _frameRate = 1;
        }
        else
        {
            _frameRate++;
        }
    #endif
    
    GL_ERROR=======>1282 这个错误,是否因为我对这个库用错了,还是因为其他的原因,有点不懂 
    [self.view addSubview:myview];执行后,需要点时间来创建缓冲区,不能立马显示数据。
    你可以把创建和addsubview提前到别的地方
    
    或者延后点再调用displayYUV420pData
    
    看效果可以这样
    [myview displayYUV420pData:pFrameRGB 100 height:100];
    改成
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(1);
            [glView displayYUV420pData:data 100 height:100];
        }); 
    这个类有个bug:动态创建的view,有时候显示图像显示不出来,卡的要死,内存还会不断增大, 帧率打印是 1帧/秒。
    
    治标的办法有2种:
    
    1.把创建view放到viewDidLoad或者更早的地方,创建好后添加到视图上,这种不会出问题。
    
    2.如果实在是需要动态创建,则创建并添加到视图上后,执行glview.layer.borderWidth = 1.0;glview.layer.borderWidth = 0;相当于刷新下视图,然后再调用display。这种需要找合适的时机刷新,刷新和渲染最好有时间间隔。
    
    这个bug的原因我不清楚,貌似OpenGL的conext绑定有问题。也找不到彻底的解决方案。有谁知道原因的话,告诉我,谢谢。
    就是这里描述的问题 http://www.cocoachina.com/bbs/read.php?tid=110721 
    #import "SkyViewController.h"
    #import "OpenGLView20.h"
    
    @interface SkyViewController ()
    //@property OpenGLView20* opengl;
    @property (nonatomic , strong) OpenGLView20* opengl;
    @property (nonatomic , strong) NSData* yuvData;
    @property (nonatomic , strong) NSData* yuvData1;
    @end
    
    @implementation SkyViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        self.yuvData = [NSData dataWithContentsOfFile:@"/Users/sky/Desktop/yuvtest/yuvtest/201461110423.yuv"];
        self.yuvData1 = [NSData dataWithContentsOfFile:@"/Users/sky/Desktop/yuvtest/yuvtest/2014611103511.yuv"];
        NSLog(@"the reader length is %lu", (unsigned long)self.yuvData.length);
        self.opengl =[[OpenGLView20 alloc]initWithFrame:CGRectMake(0, 0, 1024, 768)];
        [self.view addSubview:self.opengl];
        
    }
    
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        
        UInt8 * pFrameRGB = (UInt8*)[self.yuvData bytes];
        UInt8 * pFrameRGB1 = (UInt8*)[self.yuvData1 bytes];
      
        
        NSLog(@"window before added %@", self.opengl.window);
        [self.opengl setVideoSize:1280 height:720];
        int n = 12;
        while (n--) {
            [self.opengl displayYUV420pData:pFrameRGB 1280 height:720];
            [self.opengl displayYUV420pData:pFrameRGB1 1280 height:720];
        }
    
    
    
        
    }
    
    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
    }
    
    @end
    
    那个文件里面不是有个帧率么,虽说那个算法不是很准确,也差不多,那个里面算出来的。我观察了一下,播放24帧用了3秒多,不过这个是在模拟器里面运行的,毕竟模拟器没有硬件来处理这些东西,在真机里面还没测,测了之后告诉你 

      上面罗列的内容就是帖子的关键点,我是看了这些跟贴才整出可运行的demo的,搞这个demo的过程中遇到各种问题,费神啊,还好经过几个回合终于搞定了。

      下面贴出我的代码,也就是上面帖子中谈到的关键地方:

    //
    //  ViewController.m
    //  OpenGLESRenderYUVImage
    //
    //  Created by dev.temobi on 15/4/29.
    //  Copyright (c) 2015年 dev.temobi. All rights reserved.
    //
    
    #import "ViewController.h"
    
    #import "OpenGLView20.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    {
        NSData *yuvData;
        OpenGLView20 * myview;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        NSString *yuvFile = [[NSBundle mainBundle] pathForResource:@"jpgimage1_image_640_480" ofType:@"yuv"];
        yuvData = [NSData dataWithContentsOfFile:yuvFile];
        NSLog(@"the reader length is %lu", (unsigned long)yuvData.length);
        
        myview = [[OpenGLView20 alloc]initWithFrame:CGRectMake(20, 20, self.view.frame.size.width - 40, self.view.frame.size.height - 40)];
        [self.view addSubview:myview];
    }
    
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        
        UInt8 * pFrameRGB = (UInt8*)[yuvData bytes];
        [myview setVideoSize:640 height:480];
        [myview displayYUV420pData:pFrameRGB 640 height:480];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end

      贴一张demo的运行效果图:

      我写的完整demo -- OpenGLESRenderYUVImage 下载地址为:http://pan.baidu.com/s/1hq09qoG

  • 相关阅读:
    lua 中的上n级模块路径函数分享
    [poj 1062] 昂贵的聘礼
    [poj 2479] Maximum sum -- 转载
    IT界天才少年:比肩雷军、叫板任正非,自己作死了
    chromedriver版本 支持的Chrome版本
    运维开发:python websocket网页实时显示远程服务器日志信息
    JVM理论:(三/4)方法调用
    JVM理论:(三/3)运行时栈帧结构、基于栈的字节码解释执行过程
    JVM理论:(三/2)字节码指令
    JVM理论:(三/1)class类文件结构
  • 原文地址:https://www.cnblogs.com/sunminmin/p/4468891.html
Copyright © 2020-2023  润新知