實戰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 width: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 width: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 width:100 height:100];
}


我這樣寫,yuv的圖像總是顯示不出來,現在出現的問題是在文件的這個地方

#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("視頻 %d 幀率:   %d\n", 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 width:100 height:100];
改成
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        sleep(1);
        [glView displayYUV420pData:data width: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 width:1280 height:720];
        [self.opengl displayYUV420pData:pFrameRGB1 width: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 width: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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM