在之前透過 AVCaptureStillImageOutput 做靜態影像的擷取文章中,我們已經知道如何建立 AVCaptureSession,並且透過對應的 AVCaptureDeviceInput 與 AVCaptureStillImageOutput 來擷取靜態影像,下面我們將更換此 AVCaptureSession 的 Output 部分,使用 AVCaptureVideoDataOutput 來代替原先的 AVCaptureStillImageOutput,你可以參考之前的文章對 AVCaptureSession 與 AVCaptureDeviceInput 做設定,下面程式碼就只針對 AVCaptureVideoDataOutput 部分做說明。
建立 AVCaptureVideoDataOutput
想要擷取影片中的連續影像片段,就可以考慮使用 AVCaptureVideoDataOutput 來當做你的輸出端,你可以在 AVCaptureVideoDataOutput 中設定影像序列每秒的張數(FPS),並且透過 CMSampleBufferRef 來針對每一張所擷取的影像做額外的處理。
在這部份中我們要在類別的 @interface 區段中宣告一個 AVCaptureVideoDataOutput 型態的全域變數,並且替專加入 CoreVideo、CoreMedia 兩個 Framework 與對應的標頭檔,方便之後的作業。
AVCaptureVideoDataOutput *myVideoDataOutput;
接著,在根據以下程式碼對 AVCaptureVideoDataOutput 做設定,並將此 Output 與 AVCaptureSession 做連接。
myVideoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
myVideoDataOutput.videoSettings = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey: (id)kCVPixelBufferPixelFormatTypeKey];
//[myVideoDataOutput setAlwaysDiscardsLateVideoFrames:YES];
//[myVideoDataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
AVCaptureConnection *connection = [myVideoDataOutput connectionWithMediaType:AVMediaTypeVideo];
[connection setVideoMaxFrameDuration:CMTimeMake(1, 20)];
[connection setVideoMinFrameDuration:CMTimeMake(1, 10)];
[[self myCaptureSession] addOutput:myVideoDataOutput];
在設定 AVCaptureVideoDataOutput 上,我們將所擷取到的影像做正規劃成 BGRA 32位元的的像素格式,也是為了後續如果要針對影像做處理時,方便我們作業。
之後,暫且略過程式碼設註解的地方,我們從 AVCaptureVideoDataOutput 中取得 AVCaptureConnection 做每秒張數張的設定,這些設定將會反應在之後的 CMSampleBufferRef 上。
之後,暫且略過程式碼設註解的地方,我們從 AVCaptureVideoDataOutput 中取得 AVCaptureConnection 做每秒張數張的設定,這些設定將會反應在之後的 CMSampleBufferRef 上。
取得 CMSampleBufferRef
在成功建立好 AVCaptureSession 的 Output 之後,接下來就是製作連續影像片段的擷取。
連續影像片段的擷取是全自動的,它是透過協定 Protocol 的方式,每當取得 CMSampleBufferRef 就會去呼叫對應的函式,所以在開始之前我們要回到類別中的 @interface 區段去增加 <AVCaptureVideoDataOutputSampleBufferDelegate> 協定,之後我們才能在對應的函式中實作處理 CMSampleBufferRef 的程式碼。
首先,取消在上個段落中我們註解的那兩行程式碼,現在我們的程式具有「丟棄延遲的影格」與「擷取連續影像片段」的兩個功能,剛好對應下列這兩個不同的方法函式(這兩個方法函式必須在使用 <AVCaptureVideoDataOutputSampleBufferDelegate> 協定之後才會出現)。
連續影像片段的擷取是全自動的,它是透過協定 Protocol 的方式,每當取得 CMSampleBufferRef 就會去呼叫對應的函式,所以在開始之前我們要回到類別中的 @interface 區段去增加 <AVCaptureVideoDataOutputSampleBufferDelegate> 協定,之後我們才能在對應的函式中實作處理 CMSampleBufferRef 的程式碼。
首先,取消在上個段落中我們註解的那兩行程式碼,現在我們的程式具有「丟棄延遲的影格」與「擷取連續影像片段」的兩個功能,剛好對應下列這兩個不同的方法函式(這兩個方法函式必須在使用 <AVCaptureVideoDataOutputSampleBufferDelegate> 協定之後才會出現)。
//丟棄延遲的影格
- (void)captureOutput:(AVCaptureOutput *)captureOutput didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {}
//擷取連續影像片段
- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection {}
CMSampleBufferRef 與 UIImage 的轉換
在取得 CMSampleBufferRef 之後,還必須透過一連串的轉換才能夠得到 UIImage,CMSampleBufferRef --> CVImageBufferRef --> CGContextRef --> CGImageRef --> UIImage,你可以將以下程式碼任意實作於上述兩個內建函式中來取得連續影像片段中的 UIImage。
//製作 CVImageBufferRef
CVImageBufferRef buffer;
buffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(buffer, 0);
//從 CVImageBufferRef 取得影像的細部資訊
uint8_t *base;
size_t width, height, bytesPerRow;
base = CVPixelBufferGetBaseAddress(buffer);
width = CVPixelBufferGetWidth(buffer);
height = CVPixelBufferGetHeight(buffer);
bytesPerRow = CVPixelBufferGetBytesPerRow(buffer);
//利用取得影像細部資訊格式化 CGContextRef
CGColorSpaceRef colorSpace;
CGContextRef cgContext;
colorSpace = CGColorSpaceCreateDeviceRGB();
cgContext = CGBitmapContextCreate(base, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
//透過 CGImageRef 將 CGContextRef 轉換成 UIImage
CGImageRef cgImage;
UIImage *image;
cgImage = CGBitmapContextCreateImage(cgContext);
image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
CGContextRelease(cgContext);
CVPixelBufferUnlockBaseAddress(buffer, 0);
//成功轉換成 UIImage
//[myImageView setImage:image];
最後,如果你希望改變擷取影像時的方向,則可以對內建函式中的 AVCaptureConnection 做 setVideoOrientation: 旋轉影像,或 setVideoMirrored: 鏡射影像。
沒有留言:
張貼留言