IOS:Camera的特性分析與使用3_OPENGL特效
本來想用2個篇幅結束Camera軟體部分的介紹,後來發現,非常重要的一點OpenGL還沒介紹,所以又增加了這一篇。
這篇主要描述一下幾個方面的內容:
(1)錄影介面OPENGL展示
(2)錄影即時特效處理
(3)視頻等比例縮放、旋轉 如:等比例、16:9 4:3 1:1等
這個部分我思來想去缺失不太好講,設計到的知識太多,尤其是OpenGL的一些專業知識,通過一篇部落格普及OpenGL的知識顯然不科學,所以只能瞭解一個流程,至於裡面到底是怎麼回事,請大家找本OpenGL的書看看,我想等這幾個部落格完工之後,也寫幾篇OpenGL的部落格呵呵。
我們的整個流程是,首先從AVCaptureSession拿到視頻拍攝時候的資料流,然後特效處理(特效這塊可以參考另一個Image&Animation專欄),然後初始化OpenGL開始進行紋理貼圖。
(1)如何拿到視頻資料流?
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
if ( videooutput == captureOutput ) {
OSStatus err = CMBufferQueueEnqueue(previewBufferQueue, sampleBuffer);
if ( !err ) {
dispatch_async(dispatch_get_main_queue(), ^{
CMSampleBufferRef sbuf = (CMSampleBufferRef)CMBufferQueueDequeueAndRetain(previewBufferQueue);
if (sbuf) {
CVImageBufferRef pixBuf = CMSampleBufferGetImageBuffer(sbuf);
if (effectflag) {
特效處理
}
OpenGL紋理展示
CFRelease(sbuf);
}
});
}
}
}
AVCaptureSession初始化完成之後我們可以設定一個回調方法,在這個回調方法中,可以很方便的拿到我們需要處理的映像資料。
(2)如何進行圖片的特效處理
這又是一個非常複雜的內容,我也專門為此寫了另外一篇部落格:
這中間牽扯到各種影像處理演算法,neon、彙編最佳化,ARM內部寄存器的使用等等。
這裡我們只說如何吧ImageBuffer轉化為RGBA像素:
unsigned char *pixel = (unsigned char *)CVPixelBufferGetBaseAddress(pixelBuffer);
這裡pixel存放的就是圖片的RGBA像素值。
(3)OpenGL紋理貼圖
3.1//在使用Opengles的時候需要重構CAEAGLLayer圖層
+ (Class)layerClass
{
return [CAEAGLLayer class];
}
3.2// 設定CAEAGLLayer
CAEAGLLayer*eaglLayer = (CAEAGLLayer *)self.layer;
3.3設定CAEAGLLayer層的屬性RGBA8
3.4// 使用opengl2.0 建立映像繪製上下文
oglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
3.5// 設定oglContext為當前上下文
3.2 // glEnable(GL_DEPTH_TEST):用來開啟更新深度緩衝區的功能
3.3 // 建立框架緩衝區
3.4 // 講框架緩衝區綁定在繪圖管線上
3.5 // 建立繪圖緩衝區
3.6 // 講繪圖緩衝區綁定在管線上
3.7 // 為繪圖緩衝區(或者叫渲染緩衝區分配空間)
3.8 // 擷取當前繪圖緩衝區(渲染緩衝區的)寬和高
3.9 // 講渲染緩衝區與框架緩衝區綁定在一起
3.10 // 檢查當前框架緩衝區的狀態是否有效
3.11 // 建立一個opengl的紋理對象
3.12 // 載入定點和片段著色器
3.13 // 建立並初始化這個工程對象
對應代碼如下:
//在使用Opengles的時候需要重構CAEAGLLayer圖層
+ (Class)layerClass
{
return [CAEAGLLayer class];
}
- (BOOL)initializeBuffers
{
// 設定oglContext為當前上下文
if ([EAGLContext currentContext] != oglContext) {
if ([EAGLContext setCurrentContext:oglContext]) {
NSLog(@setCurrentContext error... ...);
}
}
BOOL success = YES;
// 設定圖層的frame和bounds
CGRect rtFullscreem = [[UIScreen mainScreen] bounds];
CGRect rtCurrframe = self.layer.frame;
CGRect rtCurrbounds = self.layer.bounds;
self.layer.frame = rtFullscreem;
self.layer.bounds = rtFullscreem;
NSLog(@size{%f %f %f %f}, rtFullscreem.origin.x, rtFullscreem.origin.x, rtFullscreem.size.width, rtFullscreem.size.height);
// glEnable(GL_DEPTH_TEST): 用來開啟更新深度緩衝區的功能,也就是,如果通過比較後深度值發生變化了,會進行更新深度緩衝區的操作。啟動它,OpenGL就可以跟蹤再Z軸上的像素,這樣,它只會再那個像素前方沒有東西時,才會繪畫這個像素。
// 一般這個功能開啟之後繪製3D效果比較好
glDisable(GL_DEPTH_TEST);
// 建立框架緩衝區
glGenFramebuffers(1, &frameBufferHandle);
// 講框架緩衝區綁定在繪圖管線上
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferHandle);
// 建立繪圖緩衝區
glGenRenderbuffers(1, &colorBufferHandle);
// 講繪圖緩衝區綁定在管線上
glBindRenderbuffer(GL_RENDERBUFFER, colorBufferHandle);
// 為繪圖緩衝區(或者叫渲染緩衝區分配空間)
[oglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
// 擷取當前繪圖緩衝區(渲染緩衝區的)寬和高
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &renderBufferWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &renderBufferHeight);
// 講渲染緩衝區與框架緩衝區綁定在一起
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle);
// 檢查當前框架緩衝區的狀態是否有效
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@Failure with framebuffer generation 0x%X, glCheckFramebufferStatus(GL_FRAMEBUFFER));
success = NO;
}
// Create a new CVOpenGLESTexture cache
// 建立一個opengl的紋理對象
// 在oglContext 中建立紋理對象
CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, oglContext, NULL, &videoTextureCache);
if (err) {
NSLog(@Error at CVOpenGLESTextureCacheCreate %d, err);
success = NO;
}
// Load vertex and fragment shaders
// 載入定點和片段著色器
const GLchar *vertSrc = str_passThrough_v;//[self readFile:@passThrough.vsh];
const GLchar *fragSrc = str_passThrough_f;// [self readFile:@passThrough.fsh];
// attributes
GLint attribLocation[NUM_ATTRIBUTES] = {
ATTRIB_VERTEX, ATTRIB_TEXTUREPOSITON,
};
GLchar *attribName[NUM_ATTRIBUTES] = {
position, textureCoordinate,
};
// 建立並初始化這個工程對象
glueCreateProgram(vertSrc, fragSrc,
NUM_ATTRIBUTES, (const GLchar **)&attribName[0], attribLocation,
0, 0, 0, // we don't need to get uniform locations in this example
&passThroughProgram);
if (!passThroughProgram)
success = NO;
self.layer.frame = rtCurrframe;
self.layer.bounds = rtCurrbounds;
return success;
}
最後我們再來看看如何對所播放的視頻螢幕進行等比例縮放,16:9等縮放
這裡我們首先需要設定一個屬性:
glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);
而textureVertices 是一個數組,用於進行紋理貼圖時畫面設定:
全螢幕播放
GLfloat squareVertices0[8] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
等比例展開
GLfloat squareVertices1[8] = {
-0.5625f, -1.0f,
0.5625f, -1.0f,
-0.5625f, 1.0f,
0.5625f, 1.0f
};
這個資料是啥意思呢?看下面兩個圖
螢幕拍攝為1920*1080,所以1080/1920=0.5625.注意拍攝時候 寬高倒置。