「カメラ機能をアプリにつけたいけどシャッター音を鳴らしたくない」とか、「カメラ起動時のアニメーションが嫌だ」とか、カメラ機能をもっと自由にカスタマイズしたい場合は、 UIImagePickerController を使うのではなく AVFoundation フレームワークを使う必要があります。
静止画撮影はデフォルトの挙動としてシャッター音がなってしまうようになっていてそこは変えられないので、動画モードで撮影を開始し、必要なフレームを静止画として取り込むことで「シャッター音の鳴らないカメラ」を実現します。
以下、プレビュー表示と「シャッター音の鳴らない」撮影ボタンだけからなる、シンプルな無音カメラアプリの実装手順を説明します。
##準備
以下のフレームワークをプロジェクトに追加します。
- AVFoundation.framework
- AssetsLibrary.framework
- CoreMedia.framework
- CoreVideo.framework
##ヘッダでの宣言
撮影を制御するのに使用するフラグと、ビットマップ保存領域用のポインタを定義します。
BOOL isRequireTakePhoto;
BOOL isProcessingTakePhoto;
void *bitmap;
また、撮影画像を保持する UIImage 型のプロパティをイメージバッファを定義します。
@property (nonatomic, retain) UIImage *imageBuffer;
AVCaptureVideoDataOutputSampleBufferDelegate プロトコルへの準拠を宣言します。
<AVCaptureVideoDataOutputSampleBufferDelegate>
##カメラ初期化処理の実装
まず、撮影画像を保持するためのバッファを確保します。
// バッファ作成
size_t width = 640;
size_t height = 480;
bitmap = malloc(width * height * 4);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef dataProviderRef = CGDataProviderCreateWithData(NULL, bitmap, width * height * 4, NULL);
CGImageRef cgImage = CGImageCreate(width, height, 8, 32, width * 4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst, dataProviderRef, NULL, 0, kCGRenderingIntentDefault);
self.imageBuffer = [UIImage imageWithCGImage:cgImage];
CGColorSpaceRelease(colorSpace);
CGDataProviderRelease(dataProviderRef);
次に、カメラデバイスと入力、セッションを初期化します。
// カメラデバイスの初期化
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 入力の初期化
NSError *error = nil;
AVCaptureInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice
error:&error];
// セッション初期化
AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];
[captureSession addInput:captureInput];
[captureSession beginConfiguration];
captureSession.sessionPreset = AVCaptureSessionPreset640x480;
[captureSession commitConfiguration];
出力を初期化します。今回は動画の撮影なので、AVCaptureVideoDataOutput を使います。(静止画の撮影では出力先として AVCaptureStillImageOutput を使います)
AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
[captureSession addOutput:videoOutput];
デリゲートを下記のように指定します。こうすることで、AVCaptureVideoDataOutputSampleBufferDelegate プロトコルの captureOutput:didOutputSampleBuffer:fromConnection: が毎フレーム呼ばれるようになります。
dispatch_queue_t queue = dispatch_queue_create("com.overout223.myQueue", NULL);
[videoOutput setSampleBufferDelegate:self
queue:queue];
dispatch_release(queue);
##シャッターボタンのアクションを実装
ここでは「写真撮る」というフラグを立てるだけです。
- (IBAction)pressShutter {
if (!isProcessingTakePhoto) {
isRequireTakePhoto = YES;
}
}
##動画のフレーム取得時の処理を実装
カメラが動画のフレームを取得する度に(つまり撮影時の毎フレーム)、AVCaptureVideoDataOutputSampleBufferDelegateプロトコルのcaptureOutput:didOutputSampleBuffer:fromConnection:メソッドが呼ばれるので、そこで必要に応じて画像の保存処理を行います。
ユーザーがシャッターボタンを押すと isRequireTakePhoto フラグが YES になっているので、そのときだけカメラロールへの保存処理を行うようにします。
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection {
if (isRequireTakePhoto) {
isRequireTakePhoto = NO;
isProcessingTakePhoto = YES;
CVPixelBufferRef pixbuff = CMSampleBufferGetImageBuffer(sampleBuffer);
if(CVPixelBufferLockBaseAddress(pixbuff, 0) == kCVReturnSuccess){
memcpy(bitmap, CVPixelBufferGetBaseAddress(pixbuff), 640 * 480 * 4);
CMAttachmentMode attachmentMode;
// メタデータ取得&orientation情報追記
CFDictionaryRef metadataRef = CMGetAttachment(sampleBuffer, CFSTR("MetadataDictionary"), &attachmentMode);
NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary *)CFBridgingRelease(metadataRef)];
// ここではorientaionは一定(6)とする
[metadata setObject:[NSNumber numberWithInt:6]
forKey:(NSString *)kCGImagePropertyOrientation];
// フォトアルバムに保存
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:self.imageBuffer.CGImage
metadata:metadata
completionBlock:^(NSURL *assetURL, NSError *error) {
NSLog(@"URL:%@", assetURL);
NSLog(@"error:%@", error);
isProcessingTakePhoto = NO;
}];
CVPixelBufferUnlockBaseAddress(pixbuff, 0);
}
}
}
##サンプルコード
今回のサンプルコードは、gumroad よりダウンロードしていただけます。
(すいません、無料ではなく、85円です)
- iOS 6.0 で動作確認しております。
- コードのみの販売です。サポート等はご容赦ください。
##参考書籍
上記サンプルは、次の書籍を参考に実装しました。
こちらの第3章『マルチメディア』の章とサンプルコードが非常に参考になります。