iOS Camera 視頻流數(shù)據(jù)綁定texture

本文檔將介紹iOS Camera回調(diào)的視頻數(shù)據(jù)如何轉(zhuǎn)換為后續(xù)OpenGL圖像渲染所需要的texture溉愁,并介紹幾種常用顏色存儲(chǔ)的數(shù)據(jù)形式轉(zhuǎn)換texture的方式仆救。

1秸抚、Camera回調(diào)視頻數(shù)據(jù)

camera的回調(diào)數(shù)據(jù)接口:

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

回調(diào)回來sampleBuffer數(shù)據(jù):

CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);

OSType result = CVPixelBufferGetPixelFormatType(pixelBuffer);

通過CVPixelBufferGetPixelFormatType看到所獲取視頻數(shù)據(jù)類型感帅,數(shù)據(jù)類型其實(shí)是在配置Camera時(shí)候設(shè)置的:

@property(nonatomic, copy) NSDictionary *videoSettings;

常用的3中類型為:

kCVPixelFormatType_32RGBA        = 'RGBA',    /* 32 bit RGBA */    

kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange = '420v', /* Bi-Planar Component Y'CbCr 8-bit 4:2:0, video-range (luma=[16,235] chroma=[16,240]).  baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */

kCVPixelFormatType_420YpCbCr8BiPlanarFullRange  = '420f', /* Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255]).  baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */

后兩種其實(shí)就是視頻處理中的420sp圆雁,在ios中排列形式都是NV12,接下來我會(huì)對(duì)以上的三種形式和yuv420p進(jìn)行texture的生成为黎。

2邮丰、 BGRA8888形式的綁定

- (BOOL)setupOriginTextureWithPixelBuffer:(CVPixelBufferRef)pixelBuffer
{
CVReturn cvRet = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                              _cvTextureCache,
                                                              pixelBuffer,
                                                              NULL,
                                                              GL_TEXTURE_2D,
                                                              GL_RGBA,
                                                              self.imageWidth,
                                                              self.imageHeight,
                                                              GL_BGRA,
                                                              GL_UNSIGNED_BYTE,
                                                              0,
                                                              &_cvTextureOrigin);

if (!_cvTextureOrigin || kCVReturnSuccess != cvRet) {
    
    NSLog(@"CVOpenGLESTextureCacheCreateTextureFromImage %d" , cvRet);
    
    return NO;
}

_textureOriginInput = CVOpenGLESTextureGetName(_cvTextureOrigin);
glBindTexture(GL_TEXTURE_2D , _textureOriginInput);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
glBindTexture(GL_TEXTURE_2D, 0);

return YES;
}

通過使用CVOpenGLESTextureCacheCreateTextureFromImage,將BGRA的數(shù)據(jù)轉(zhuǎn)換到RGBA的紋理上铭乾。

3剪廉、對(duì)于420f或者420v形式的綁定

使用CVOpenGLESTextureCacheCreateTextureFromImage創(chuàng)建Y、UV兩個(gè)紋理炕檩,并在fragment shader中轉(zhuǎn)換為RGBA

- (BOOL)setupLumaTextureWithPixelBuffer:(CVPixelBufferRef)pixelBuffer
{
//Y-plane
glActiveTexture(GL_TEXTURE0);
CVReturn cvRet = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                   _cvTextureCache,
                                                   pixelBuffer,
                                                   NULL,
                                                   GL_TEXTURE_2D,
                                                   GL_LUMINANCE,
                                                   self.imageWidth,
                                                   self.imageHeight,
                                                   GL_LUMINANCE,
                                                   GL_UNSIGNED_BYTE,
                                                   0,
                                                   &_cvlumaTexture);
if (!_cvlumaTexture || kCVReturnSuccess != cvRet) {
    
    NSLog(@"CVOpenGLESTextureCacheCreateTextureFromImage %d" , cvRet);
    
    return NO;
}

_textureLuma = CVOpenGLESTextureGetName(_cvlumaTexture);
glBindTexture(CVOpenGLESTextureGetTarget(_cvlumaTexture), _textureLuma);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
//glBindTexture(GL_TEXTURE_2D, 0);

// UV-plane.
glActiveTexture(GL_TEXTURE0);
cvRet = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                   _cvTextureCache,
                                                   pixelBuffer,
                                                   NULL,
                                                   GL_TEXTURE_2D,
                                                   GL_LUMINANCE_ALPHA,
                                                   self.imageWidth / 2,
                                                   self.imageHeight / 2,
                                                   GL_LUMINANCE_ALPHA,
                                                   GL_UNSIGNED_BYTE,
                                                   1,
                                                   &_cvchromaTexture);
if (!_cvchromaTexture || kCVReturnSuccess != cvRet) {
    
    NSLog(@"CVOpenGLESTextureCacheCreateTextureFromImage %d" , cvRet);
    
    return NO;
}

_textureChroma = CVOpenGLESTextureGetName(_cvchromaTexture);
glBindTexture(CVOpenGLESTextureGetTarget(_cvchromaTexture), _textureChroma);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
//glBindTexture(GL_TEXTURE_2D, 0);

return YES;
}

internalFormat斗蒋、format參數(shù)設(shè)置:GL_LUMINANCE 、GL_LUMINANCE_ALPHA
可以嘗試GL_RED_EXT笛质、GL_RG_EXT泉沾,應(yīng)該會(huì)有顏色上的變化。

對(duì)應(yīng)的fragment shader
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:

 //yuv420f/v
char fsh1[] = "varying highp vec2 textureCoordinate;\
precision mediump float;\
uniform sampler2D SamplerY;\
uniform sampler2D SamplerUV;\
uniform mat3 colorConversionMatrix;\
void main()\
{\
mediump vec3 yuv;\
lowp vec3 rgb;\
yuv.x = (texture2D(SamplerY, textureCoordinate).r);\
yuv.yz = (texture2D(SamplerUV, textureCoordinate).ra - vec2(0.5, 0.5));\
rgb = colorConversionMatrix * yuv;\
gl_FragColor = vec4(rgb,1);\
}";

kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:

//yuv420f/v
char fsh4[] = "varying highp vec2 textureCoordinate;\
precision mediump float;\
uniform sampler2D SamplerY;\
uniform sampler2D SamplerUV;\
uniform mat3 colorConversionMatrix;\
void main()\
{\
mediump vec3 yuv;\
lowp vec3 rgb;\
yuv.x = (texture2D(SamplerY, textureCoordinate).r - (16.0 / 255.0));\
yuv.yz = (texture2D(SamplerUV, textureCoordinate).ra - vec2(0.5, 0.5));\
rgb = colorConversionMatrix * yuv;\
gl_FragColor = vec4(rgb,1);\
}";

4妇押、yuv420p

//glGenTextures(1, &(_textureY));
glBindTexture(GL_TEXTURE_2D, _textureY);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, self.imageWidth, self.imageHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, yuv_Y);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);

//glGenTextures(1, &(_textureU));
glBindTexture(GL_TEXTURE_2D, _textureU);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, self.imageWidth/2, self.imageHeight/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, yuv_U);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);

//glGenTextures(1, &(_textureV));
glBindTexture(GL_TEXTURE_2D, _textureV);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, self.imageWidth/2, self.imageHeight/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, yuv_V);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);

對(duì)應(yīng)的fragment shader:

char fsh3[] = "varying highp vec2 textureCoordinate;\
precision mediump float;\
uniform sampler2D SamplerY;\
uniform sampler2D SamplerU;\
uniform sampler2D SamplerV;\
uniform mat3 colorConversionMatrix;\
void main()\
{\
mediump vec3 yuv;\
lowp vec3 rgb;\
yuv.x = (texture2D(SamplerY, textureCoordinate).r - (16.0 / 255.0));\
yuv.y = (texture2D(SamplerU, textureCoordinate).r - 0.5);\
yuv.z = (texture2D(SamplerV, textureCoordinate).r - 0.5);\
rgb = colorConversionMatrix * yuv;\
gl_FragColor = vec4(rgb,1);\
}";

5跷究、調(diào)試

首先可以通過Debug/Capture GPU Frame來抓取一幀畫面


Snip20170809_7.png

這樣可以看到Y(jié)、UV紋理有沒有創(chuàng)建成功敲霍,并且給出類RenderBuffer的渲染情況俊马。

每一幀數(shù)據(jù)都需要flush

if (_cvchromaTexture) {
    
    CFRelease(_cvchromaTexture);
    _cvchromaTexture = NULL;
}

if (_cvlumaTexture) {
    
    CFRelease(_cvlumaTexture);
    _cvlumaTexture = NULL;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肩杈,隨后出現(xiàn)的幾起案子潭袱,更是在濱河造成了極大的恐慌,老刑警劉巖锋恬,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屯换,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)彤悔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門嘉抓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晕窑,你說我怎么就攤上這事抑片。” “怎么了杨赤?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵敞斋,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我疾牲,道長(zhǎng)植捎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任阳柔,我火速辦了婚禮焰枢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舌剂。我一直安慰自己济锄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布霍转。 她就那樣靜靜地躺著荐绝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪避消。 梳的紋絲不亂的頭發(fā)上低滩,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音沾谓,去河邊找鬼。 笑死戳鹅,一個(gè)胖子當(dāng)著我的面吹牛均驶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播枫虏,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妇穴,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了隶债?” 一聲冷哼從身側(cè)響起腾它,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎死讹,沒想到半個(gè)月后瞒滴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年妓忍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虏两。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡世剖,死狀恐怖定罢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情旁瘫,我是刑警寧澤祖凫,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站酬凳,受9級(jí)特大地震影響惠况,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粱年,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一售滤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧台诗,春花似錦完箩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至粱快,卻和暖如春秩彤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背事哭。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工漫雷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鳍咱。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓降盹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谤辜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蓄坏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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