前回は、ffmpegを使用した動画編集についてご紹介しました。
ようやく、スマートフォンでライブ配信を行うために必要な工程が明確になりました。
今回は、スマートフォンでカメラ起動して、フレーム単位で画像保存する方法についてご紹介したいと思います。
iPhone6で動作検証を行っていきたいと思います。
マルチプラットフォームを利用せず、Xcodeを利用してご紹介していきたいと思います。
それでは、実際にアプリケーションを作成していきましょう。
<アプリケーションの作成方法>
【前提条件】
・開発環境である「Xcode」を利用すること
※カメラ撮影部分、フレーム単位で画像保存する処理以外を作成しておくこと
【手順1】
事前準備で用意したプロジェクトを開きます。
【手順2】
実際に、カメラで撮影している最中に、フレーム単位で画像を保存する処理を作成します。
下記のiOS標準framworkを利用して実現させます。
・AVFoundation:動画をフレーム単位で切り出す際に利用する
カメラ起動 〜 撮影までの実現方法は、カメラからフレーム単位で画像を取得し、
その取得した画像を連続表示させることで擬似的に動画を撮影しているようにしています。
手順としては、下記となります。
1.「AVCaptureDevice」というiOS標準クラスを利用し、カメラを起動します
2.「AVCaptureDeviceInput」というiOS標準クラスを利用し、カメラの入力情報を受け取れるようにします
3.「AVCaptureVideoDataOutput」というiOS標準クラスを利用し、フレーム単位の画像を出力します
実際のプログラムは、下記になります。
- (void)startMovie { NSError *error = nil; //入力と出力からキャプチャーセッションを作成 self.session = [[AVCaptureSession alloc] init]; self.session.sessionPreset = AVCaptureSessionPresetMedium; //カメラからの入力を作成 AVCaptureDevice *camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; //カメラからの入力を作成し、セッションに追加 self.videoInput = [AVCaptureDeviceInput deviceInputWithDevice:camera error:&error]; [self.session addInput:self.videoInput]; //画像への出力を作成し、セッションに追加 self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; [self.session addOutput:self.videoDataOutput]; //ビデオ出力のキャプチャの画像情報のキューを設定 dispatch_queue_t queue = dispatch_queue_create("videoQueue", NULL); [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:TRUE]; [self.videoDataOutput setSampleBufferDelegate:self queue:queue]; //ビデオへの出力の画像は、BGRAで出力 self.videoDataOutput.videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] }; // ビデオ入力のAVCaptureConnectionを取得 AVCaptureConnection *videoConnection = [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo]; //フレームを指定(今回は、30フレーム) videoConnection.videoMinFrameDuration = CMTimeMake(1, 30); [self.session startRunning]; }
そして、フレーム単位での画像が取得できると、下記のメソッドがコールされるので、その時に、
取得した画像をUIImageViewControllerへ設定します。
この動作を繰り返すことで、擬似的に動画を撮影しているようになります。
//新しいキャプチャ情報が追加されたときにコールされる - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { // キャプチャしたフレームからCGImageを作成 UIImage *image = [self imageFromSampleBuffer:sampleBuffer]; // 画像を画面に表示 dispatch_async(dispatch_get_main_queue(), ^{ self.moveImageView.image = image; self.index++; [self writeCGImageTo:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] image:image index:self.index]; }); }
フレーム単位での画像が取得できると、上記のメソッドがコールされるので、その時に、
下記のメソッドで画像を任意の場所へ保存します。
※今回は、Documentへ保存しています。
/* * 画像を保存する */ - (BOOL)writeCGImageTo:(NSString*)path image:(UIImage *)image index:(NSInteger)index { NSLog(@"Called"); NSFileManager *filemanager = [ NSFileManager defaultManager]; path = [path stringByAppendingPathComponent:@"/img"]; BOOL exists = [filemanager fileExistsAtPath:path]; if (!exists) { NSError *error; BOOL created = [filemanager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; if (!created) { NSLog(@"ディレクトリ作成失敗"); } } NSString *str = [NSString stringWithFormat:@"img_%ld.jpg", (long)index]; path = [path stringByAppendingPathComponent:str]; NSData *data = UIImageJPEGRepresentation(image, 1.0); BOOL result = [data writeToFile:path atomically:YES]; return result; }
これで、フレーム単位で画像を保存する処理は完成です。
それでは、アプリを起動して、動作確認を行ってみましょう
【手順1】
「動画撮影開始」ボタンをタップし、カメラを起動します。
【手順2】
起動中、フレーム単位で保存した画像が表示されることを確認します。
お疲れ様でした。
動画をフレーム単位で画像保存することができたところで、今回は終了いたします。
最後までご覧いただきありがとうございました。
弊社では全国各地の請負い(ご自宅)で作業協力頂ける、フリーランスエンジニアの方を常時探しております。
ご興味ある方は、お気軽にお問い合わせ下さい。