本來想用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.注意拍攝時候 寬高倒置。