從kxmovie代碼看iOS上OpenGL ES的顯示流程

一句話概述:視頻的幀數(shù)據(jù)芳室,傳遞給OpenGL,處理后輸出給FBO北救,然后取得FBO里的color render buffer门怪,然后通過CAEAGLLayer上呈現(xiàn)到屏幕

想多了解下音視頻開發(fā),看了下kxmovie的代碼票髓,kxmovie是一個(gè)基于FFmpeg的iOS上的開源音視頻庫攀涵,主體就3部分:

  • FFmpeg對音視頻資源的解碼,輸出一幀幀的數(shù)據(jù)
  • 對數(shù)據(jù)的管理洽沟,如緩沖區(qū)管理以故;播放操作的管理,如停止裆操、開始
  • 把視頻數(shù)據(jù)呈現(xiàn)到屏幕上

現(xiàn)在要說的就是第三段怒详,而kxmovie就是用的OpenGL ES來處理的(確切的說是優(yōu)先使用OpenGL ES來做的)。

上代碼:

- (CGFloat) presentVideoFrame: (KxVideoFrame *) frame
{
    if (_glView) {
        
        [_glView render:frame];   //代碼1
        
    } else {
        
        KxVideoFrameRGB *rgbFrame = (KxVideoFrameRGB *)frame;
        _imageView.image = [rgbFrame asImage];
    }
    
    _moviePosition = frame.position;
        
    return frame.duration;
}

入口就在代碼1位置踪区,_glView就是用來呈現(xiàn)視頻畫面的View昆烁,而frame是一幀數(shù)據(jù)。整體就是不斷的在調(diào)用這個(gè)方法缎岗,不斷地顯示一幀幀的畫面静尼。

然后進(jìn)入render方法:

- (void)render: (KxVideoFrame *) frame
{
    
    static const GLfloat texCoords[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
    };
 
    [EAGLContext setCurrentContext:_context];  
    
    glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);  //代碼1
    glViewport(0, 0, _backingWidth, _backingHeight);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(_program);
        
    if (frame) {
        [_renderer setFrame:frame];     //代碼2   
    }
    
    if ([_renderer prepareRender]) {  //代碼3
       
        GLfloat modelviewProj[16];
        mat4f_LoadOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, modelviewProj);
        glUniformMatrix4fv(_uniformMatrix, 1, GL_FALSE, modelviewProj);
        
        glVertexAttribPointer(ATTRIBUTE_VERTEX, 2, GL_FLOAT, 0, 0, _vertices);
        glEnableVertexAttribArray(ATTRIBUTE_VERTEX);
        glVertexAttribPointer(ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, 0, 0, texCoords);
        glEnableVertexAttribArray(ATTRIBUTE_TEXCOORD);
        
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);        
    }
    //代碼4
    glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

(1)代碼1位置,綁定_framebuffer传泊,OpenGL里面有許多的類似的Bind函數(shù)鼠渺,我理解的是,這樣做之后眷细,之后所有對frameBuffer的操作就是針對這個(gè)frameBuffer的了拦盹。官方文檔里有句解釋:

While a non-zero framebuffer object name is bound, GL operations on target GL_FRAMEBUFFER
 affect the bound framebuffer object, and queries of target GL_FRAMEBUFFER
 or of framebuffer details such as GL_DEPTH_BITS
 return state from the bound framebuffer object. 

(2)說下frameBuffer Object(FBO),具體的使用流程可以參考這篇.簡單說,就是OpenGL的繪制結(jié)果不是直接顯示到屏幕上薪鹦,而是存起來了掌敬,這個(gè)存儲的東西就是FBO惯豆。所以FBO里面包含了color、depth奔害、stencil等一些用于顯示的信息楷兽。
代碼1就是指定當(dāng)前使用的FBO是哪個(gè),然后執(zhí)行后數(shù)據(jù)就會輸入到這個(gè)FBO里了华临。

(3)指定好芯杀,數(shù)據(jù)的去向,那也要指定源頭雅潭,數(shù)據(jù)從哪來揭厚。我們現(xiàn)在只有一個(gè)frame,所以要把這個(gè)frame數(shù)據(jù)變成OpenGL的輸入扶供。然后就是代碼2筛圆,函數(shù)體是:

- (void) setFrame: (KxVideoFrame *) frame
{
    KxVideoFrameRGB *rgbFrame = (KxVideoFrameRGB *)frame;
   
    assert(rgbFrame.rgb.length == rgbFrame.width * rgbFrame.height * 3);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    
    if (0 == _texture)
        glGenTextures(1, &_texture);   //代碼5
    
    glBindTexture(GL_TEXTURE_2D, _texture);//代碼6
    //代碼7
    glTexImage2D(GL_TEXTURE_2D,
                 0,
                 GL_RGB,
                 frame.width,
                 frame.height,
                 0,
                 GL_RGB,
                 GL_UNSIGNED_BYTE,
                 rgbFrame.rgb.bytes);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

整體來說,這一段的作用就是使用傳入的frame椿浓,構(gòu)建了一個(gè)紋理_texture太援。代碼5生成一個(gè)紋理,代碼6綁定紋理扳碍,也就是指定下面要操作的是_texture這個(gè)紋理提岔,代碼7應(yīng)該是賦值,最后一個(gè)參數(shù)就是數(shù)據(jù)笋敞,具體各個(gè)參數(shù)意思看文檔碱蒙。然后frame數(shù)據(jù)就轉(zhuǎn)換成了texture。

(4)有了數(shù)據(jù)紋理夯巷,把紋理傳到OpenGL的pipline里去赛惩,就是代碼3,函數(shù)體是:

- (BOOL) prepareRender
{
    if (_texture == 0)
        return NO;
    
    glActiveTexture(GL_TEXTURE0);  //代碼8
    glBindTexture(GL_TEXTURE_2D, _texture);//代碼9
    glUniform1i(_uniformSampler, 0);//代碼10
    
    return YES;
}

代碼8 選擇一個(gè)紋理槽位趁餐,即GL_TEXTURE0坊秸;9綁定紋理為_texture,這樣_texture和槽位GL_TEXTURE0聯(lián)系上了澎怒;代碼10是給shader里的變量賦值褒搔,第一個(gè)參數(shù)是指定被賦值的變量的位置,同一個(gè)shader里面會定義多個(gè)輸入對象喷面,每個(gè)都有對應(yīng)的位置星瘾,可以這樣獲得:

_uniformSampler = glGetUniformLocation(program, "s_texture");

這就是取得uniform變量s_texture的位置,賦值給_uniformSampler惧辈。代碼10第二個(gè)參數(shù)就是指定哪個(gè)紋理琳状,傳入n,就是GL_TEXTURE0+n槽位的紋理盒齿,這里傳入0念逞,結(jié)合代碼3.1和3.2困食,其實(shí)就是把_texture。然后_uniformSampler是s_texture的位置翎承,所以整體就是把_texture賦值給了shader里面的s_texture變量硕盹。
這樣,紋理數(shù)據(jù)就傳遞給了shader叨咖,進(jìn)入到OpenGL的pipline里了瘩例。

(5)OpenGL把數(shù)據(jù)輸入給我們綁定的FBO,F(xiàn)BO管理著各種buffer甸各,其中就有color buffer垛贤。代碼4位置,_renderbuffer就是綁定在當(dāng)前FBO上的color buffer趣倾,存儲著顏色信息聘惦,第一句glBindRenderbuffer指定下面對GL_RENDERBUFFER的操作是使用GL_RENDERBUFFER,然后_context顯示render buffer儒恋。然后數(shù)據(jù)就被顯示到屏幕上了部凑。

最后,render buffer的綁定代碼:

glGenFramebuffers(1, &_framebuffer);
        glGenRenderbuffers(1, &_renderbuffer);  //代碼11
        glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);   
        glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);   //代碼12
        [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];   //代碼13
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth);
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderbuffer);  //代碼14

代碼11和12就是生成和綁定一個(gè)render buffer碧浊,但實(shí)際這是render buffer只是生成了一個(gè)名字,并沒有內(nèi)存空間瘟仿,而關(guān)鍵的就是代碼13.這一句箱锐,_context從self.layer里獲取到一段內(nèi)存給新生成的render buffer,根據(jù)iOS文檔劳较,render buffer和這個(gè)CAEAGLLayer對象是共享內(nèi)存的驹止。如圖:

Core Animation shares the renderbuffer with OpenGL ES

沒看到具體文檔,但我猜這就是為什么_context調(diào)用presentRenderbuffer观蜗,然后self.layer就會更新內(nèi)容的原因臊恋,這句代碼把_context、render buffer和self.layer關(guān)聯(lián)了起來墓捻。
代碼14就是把render buffer 綁定給FBO,注意第二個(gè)參數(shù)使用GL_COLOR_ATTACHMENT0抖仅,這個(gè)指定了這個(gè)render buffer使用來存儲顏色信息的,所以O(shè)penGL把數(shù)據(jù)渲染到FBO后砖第,這個(gè)render buffer保存的是顏色信息撤卢。

參考文章:FBO的使用
OpenGL ES渲染到layeriOS官方文檔里面Rendering to a Core Animation Layer那一節(jié)
OpenGL ES2.0 – Iphone開發(fā)指引
OpenGL ES入門系列

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市梧兼,隨后出現(xiàn)的幾起案子放吩,更是在濱河造成了極大的恐慌,老刑警劉巖羽杰,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渡紫,死亡現(xiàn)場離奇詭異到推,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)惕澎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門莉测,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人集灌,你說我怎么就攤上這事悔雹。” “怎么了欣喧?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵腌零,是天一觀的道長。 經(jīng)常有香客問我唆阿,道長益涧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任驯鳖,我火速辦了婚禮闲询,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浅辙。我一直安慰自己扭弧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布记舆。 她就那樣靜靜地躺著鸽捻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泽腮。 梳的紋絲不亂的頭發(fā)上御蒲,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機(jī)與錄音诊赊,去河邊找鬼厚满。 笑死,一個(gè)胖子當(dāng)著我的面吹牛碧磅,可吹牛的內(nèi)容都是我干的碘箍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鲸郊,長吁一口氣:“原來是場噩夢啊……” “哼敲街!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起严望,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤多艇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后像吻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體峻黍,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡复隆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姆涩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挽拂。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖骨饿,靈堂內(nèi)的尸體忽然破棺而出亏栈,到底是詐尸還是另有隱情,我是刑警寧澤宏赘,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布绒北,位于F島的核電站,受9級特大地震影響察署,放射性物質(zhì)發(fā)生泄漏闷游。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一贴汪、第九天 我趴在偏房一處隱蔽的房頂上張望脐往。 院中可真熱鬧,春花似錦扳埂、人聲如沸业簿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梅尤。三九已至,卻和暖如春希太,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背厂榛。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工恨锚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窒典,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓堕澄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親霉咨。 傳聞我的和親對象是個(gè)殘疾皇子蛙紫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容