update: 2012/01/07
reference: Getting Raw Video Data into My App Quick and Dirty | Indie Ambitions
A. 建立專案
1. Xcode > File > New > New Project... > iOS > Application > OpenGL Game > Next
Product Name: RawVideoData
Device Family: iPhone
Use StroyBoard: checked
Use Automatic Reference Counting: checked
> Next
2. 接下來將會使用到藉由 core image 直接畫到 render buffer 的功能.
可以先將從樣板產生的 code 都移除(ViewController.m 中的), 我們不會使用到.
預設產生的 frameworks 如下:
會用到的.
AVFoundation
CoreVideo
CoreMedia
QuartzCore
ImageIO
CoreImage
---------------------------------------------------------------------------------------------
B. 引入 frameworks 並設定符合協定
1. 開啓 ViewController.h 檔案, 修改如下:
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
//@add
#import <AVFoundation/AVFoundation.h>
#import <CoreMedia/CoreMedia.h>
#import <CoreVideo/CoreVideo.h>
#import <QuartzCore/QuartzCore.h>
#import <CoreImage/CoreImage.h>
#import <ImageIO/ImageIO.h>
//@interface ViewController : GLKViewController
@interface ViewController : GLKViewController <AVCaptureVideoDataOutputSampleBufferDelegate>
{
//@add
AVCaptureSession *session;
CIContext *coreImageContext;
EAGLContext *context;
GLuint _renderBuffer;
}
//@add
@property (strong, nonatomic) EAGLContext *context;
@end
說明: a. 這邊所使用的委派協定(delegate protocol), 表示這個類別將會接收到由
傳送者(delivers)所回傳(callback) 的原始像素資料(raw pixel data).
AVCaptureSession 將會來作此設定, 並且配置好控制此 session 的參數,
如: 解析度, 相機輸入 .... 等. 要畫出經由濾鏡處理過的 Core Image 結果,
需要使用到 Core Image context(CIContext).
b. Core Image context 需要一個 render buffer 來寫入, 所以先在此宣告實體變數:
GLuint _renderBuffer , 之後將會拿來設定. 我們也需要一個 EAGLContext 的
參照, 來呈現 render buffer 的內容.
2. 開啓 ViewController.m 檔案, 修改如下:
(在 viewDidLoad 中, 實作設定相機與 context)
@implementation ViewController
//@add
@synthesize context = _context;
- (void)viewDidLoad
{
[super viewDidLoad];
//@add
// section 1: 大致上與樣板預設的 code 相同, 主要是設置 EAGLContext
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(@"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
// section 2: 設置 render buffer
glGenRenderbuffers(1, &_renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
//section 3: 初始化 Core Image context.
coreImageContext = [CIContext contextWithEAGLContext:self.context];
// section 4: 設置相機輸入, 建立 session 並設定. 在這邊設定 session 為 640 pixels 寬,
// 480 pixels 高. 還有其他的選項, 包括 720 pixels 跟 1080 pixels.
// Core Image 的解析度越高, 效能就越低. 一個單一簡單的濾鏡就能夠來
// 處理高解析度, 在此僅為測試的處理.
NSError *error;
session = [[AVCaptureSession alloc] init];
[session beginConfiguration];
[session setSessionPreset:AVCaptureSessionPreset640x480];
// section 5: 設定輸入設備. 假如要指定前置或後置相機, 需要呼叫
// devicesWithMediaType , 它會回傳一個設備的陣列. 若要取得前置相機,
// 可在 AVCaptureDevicePosition 屬性中, 重複地在陣列中尋找
// AVCaptureDevicePositionFront. 在此為了簡單, 我們使用預設的設備.
//
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
[session addInput:input];
// section 6: 設定輸出. 並忽略較慢的影格(frames). 如果需要記錄改變的地方, 可以
// 設定輸入資料的顏色格式.
AVCaptureVideoDataOutput *dataOutput = [[AVCaptureVideoDataOutput alloc] init];
[dataOutput setAlwaysDiscardsLateVideoFrames:YES];
[dataOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
// section 7: 設定 delegate. 它將會收到回傳(callback)的每個 frame, 並且設為主要
// 的駐列(queue).
[dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
[session addOutput:dataOutput];
// section 8: 完成設定並讓相機開始執行
[session commitConfiguration];
[session startRunning];
}
@end
---------------------------------------------------------------------------------------------
C. 回呼函式
1. 開啓 ViewController.m 檔案, 新增 code 如下: -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
// 將取得的 sampleBuffer 轉成 CVPixelBuffer
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
// 接著, 將 CVPixelBuffer 利用 Core Image 的初始化方法再轉成 CIImage.
CIImage *image = [CIImage imageWithCVPixelBuffer:pixelBuffer];
// 然後使用 CIContext 物件將其內容畫到 render buffer
[coreImageContext drawImage:image atPoint:CGPointZero fromRect:[image extent] ];
// 最後, 在 螢幕上呈現出來.
[self.context presentRenderbuffer:GL_RENDERBUFFER];
}
說明: 當要記錄 core image 濾鏡的輸出, 可以用 CIContext 的另一個方法,
來將其寫入到 CVPixelBuffer.
2. 編譯並執行: (上下顛倒, 左右相反)
開啓 ViewController.m 檔案, 修改如下:
- (void)viewDidUnload
{
[super viewDidUnload];
//@add
if ([EAGLContext currentContext] == self.context) {
[EAGLContext setCurrentContext:nil];
}
self.context = nil;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
---------------------------------------------------------------------------------------------
E. 濾鏡
1. 開啓 ViewController.m 檔案, 修改如下: //@add
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
CIImage *image = [CIImage imageWithCVPixelBuffer:pixelBuffer];
//@add filter
image = [CIFilter filterWithName:@"CIFalseColor" keysAndValues:
kCIInputImageKey, image,
@"inputColor0", [CIColor colorWithRed:0.0 green:0.2 blue:0.0],
@"inputColor1", [CIColor colorWithRed:0.0 green:0.0 blue:1.0],
nil].outputImage;
[coreImageContext drawImage:image atPoint:CGPointZero fromRect:[image extent] ];
[self.context presentRenderbuffer:GL_RENDERBUFFER];
}
說明: 假色濾鏡(False Color filter), 將圖片的內容對應到二種顏色, 在此為
暗綠與亮藍.
2. 編譯並執行: (上下顛倒, 左右相反)
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。