アウトライン
我々は、ビデオの取得パラメータをカスタマイズするためにそれを使用することができますので、放送アプリケーション、ビデオキャプチャでは、一般的に使用さAVFoundationフレームワークであり、また、携帯電話のカメラを切り替えて写真を撮って、懐中電灯や他のいくつかのカメラ操作を開くん。もちろん、もっと重要な点私たちは、業務を符号化するために使用された元の映像データを得ることができます。次のようにこの記事では、私たちの内容について説明します。
- はじめにビデオキャプチャ関連のキークラス
- ビデオキャプチャのための手順を説明します
- 解像度、フレームレート、およびプレビュー層、露光のズームアウトなどが提供され、それは、次のようなビデオキャプチャのパラメータを変更する方法について説明します。
- 例えば、カメラ動作の詳細:カメラ、レンズの前と、スイッチング開口&懐中電灯等の動作を閉じた後。
コード:
- githubの
- フォーク&スターへようこそ
主要なクラスのビデオキャプチャ
AVCaptureDevice
私たちは、このクラスから携帯電話のハードウェアカメラ、音センサーを得ることができ、ハードウェアデバイスを表します。私たちは、ハードウェアデバイスのいくつかのプロパティを変更する必要がある場合(例:フラッシュモードを変更すると、カメラのフォーカスの変更など)は、デバイスのプロパティを変更する前に呼び出されなければならないlockForConfiguration
デバイスロックのため、終了後に呼び出しを変更するunlockForConfiguration
デバイスのロックを解除する方法。
AVCaptureDeviceInput
入力デバイス管理オブジェクトは、対応するAVCaptureDeviceInput AVCaptureDevice作成されたオブジェクトは、管理AVCaptureSessionに追加されます作成することができます。これは、通常の入力装置(マイク、カメラ、等)がある、ハードウェアポートが構成されている入力装置を表します。
AVCaptureOutput
出力データを表し、出力は、ピクチャ(AVCaptureStillImageOutput)またはビデオ(AVCaptureMovieFileOutput)であってもよいです
AVCaptureSession
出力デバイスにオーディオおよびビデオデータの出力をキャプチャする責任メディアキャプチャセッション、。AVCaptureSessionは、入力または出力の複数を有することができます。これは、入出力間でデータを転送する座標AVCaptureInputとAVCaptureOutputを接続するブリッジです。それは開いて、セッションを終了するstartRunningとstopRunning二つの方法を使用しています。
アプリケーションは、この時点では、開いた構成の必要性を実行し、その後、設定後に設定を提出された:設定セッション(カメラの切り替えなど)の一部を変更する必要があれば、各セッションは、すなわち、セッションと呼ばれています。
AVCaptureConnection
AVCaptureConnectionはAVCaptureInputPortまたはポート間の接続、およびAVCaptureSessionでAVCaptureOutput又はAVCaptureVideoPreviewLayer存在を表す。それはinputPortと出力電流プレビュー画像又は層と、現在のセッションの間で接続されていること。
AVCaptureVideoPreviewPlayer
これは、画像のプレビュー層です。どのように私たちの写真や動画は、携帯電話、それに表示されますか?すなわち、のUIViewの層に添加することによって物体に、あります。
ステップビデオキャプチャ
次のコードは、ビデオキャプチャフレームレートであり30FPS
、解像度があります1920*1080
。
#import "MiVideoCollectVC.h"
#import <AVFoundation/AVFoundation.h>
@interface MiVideoCollectVC ()<AVCaptureVideoDataOutputSampleBufferDelegate>
@property (nonatomic,strong) AVCaptureVideoDataOutput *video_output;
@property (nonatomic,strong) AVCaptureSession *m_session;
@property (weak, nonatomic) IBOutlet UIView *m_displayView;
@end
@implementation MiVideoCollectVC
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self startCaptureSession];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self startPreview];
}
- (IBAction)onpressedBtnDismiss:(id)sender {
[self dismissViewControllerAnimated:YES completion:^{
[self stopPreview];
}];
}
- (void)startCaptureSession
{
NSError *error = nil;
AVCaptureSession *session = [[AVCaptureSession alloc] init];
if ([session canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {
session.sessionPreset = AVCaptureSessionPreset1920x1080;
}else{
session.sessionPreset = AVCaptureSessionPresetHigh;
}
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (error || !input) {
NSLog(@"get input device error...");
return;
}
[session addInput:input];
_video_output = [[AVCaptureVideoDataOutput alloc] init];
[session addOutput:_video_output];
// Specify the pixel format
_video_output.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]
forKey:(id)kCVPixelBufferPixelFormatTypeKey];
_video_output.alwaysDiscardsLateVideoFrames = NO;
dispatch_queue_t video_queue = dispatch_queue_create("MIVideoQueue", NULL);
[_video_output setSampleBufferDelegate:self queue:video_queue];
CMTime frameDuration = CMTimeMake(1, 30);
BOOL frameRateSupported = NO;
for (AVFrameRateRange *range in [device.activeFormat videoSupportedFrameRateRanges]) {
if (CMTIME_COMPARE_INLINE(frameDuration, >=, range.minFrameDuration) &&
CMTIME_COMPARE_INLINE(frameDuration, <=, range.maxFrameDuration)) {
frameRateSupported = YES;
}
}
if (frameRateSupported && [device lockForConfiguration:&error]) {
[device setActiveVideoMaxFrameDuration:frameDuration];
[device setActiveVideoMinFrameDuration:frameDuration];
[device unlockForConfiguration];
}
[self adjustVideoStabilization];
_m_session = session;
CALayer *previewViewLayer = [self.m_displayView layer];
previewViewLayer.backgroundColor = [[UIColor blackColor] CGColor];
AVCaptureVideoPreviewLayer *newPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_m_session];
[newPreviewLayer setFrame:[UIApplication sharedApplication].keyWindow.bounds];
[newPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
// [previewViewLayer insertSublayer:newPreviewLayer atIndex:2];
[previewViewLayer insertSublayer:newPreviewLayer atIndex:0];
}
- (void)adjustVideoStabilization
{
NSArray *devices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in devices) {
if ([device hasMediaType:AVMediaTypeVideo]) {
if ([device.activeFormat isVideoStabilizationModeSupported:AVCaptureVideoStabilizationModeAuto]) {
for (AVCaptureConnection *connection in _video_output.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
if (connection.supportsVideoStabilization) {
connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeStandard;
NSLog(@"now videoStabilizationMode = %ld",(long)connection.activeVideoStabilizationMode);
}else{
NSLog(@"connection does not support video stablization");
}
}
}
}
}else{
NSLog(@"device does not support video stablization");
}
}
}
}
- (void)startPreview
{
if (![_m_session isRunning]) {
[_m_session startRunning];
}
}
- (void)stopPreview
{
if ([_m_session isRunning]) {
[_m_session stopRunning];
}
}
#pragma mark -AVCaptureVideoDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
NSLog(@"%s",__func__);
}
// 有丢帧时,此代理方法会触发
- (void)captureOutput:(AVCaptureOutput *)output didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
NSLog(@"MediaIOS: 丢帧...");
}
@end
次のようにビデオキャプチャ具体的な手順をまとめると:
- AVCaptureSessionは、最初のオブジェクトを作成し、AVCaptrueSessionオブジェクトへの入出力装置、入出力デバイスのためにオブジェクトを追加します。
- 設定した映像の解像度はAVCaptureSessionです
- フレームレートビデオキャプチャを設定します。
- ビデオプレビュー層と層を作成することでビューに挿入されます
変更映像取得パラメータ - 解像度とフレームレート
我々は唯一の私たちは成功のために設定され、これらのパラメータを知るためには、これらのパラメータの変化を監視することができますので、我々はまず、ビデオキャプチャの面でこれらのパラメータを監視する方法を見て、私たちはビデオ解像度とフレームレートを変更する方法を説明しないようにしましょう。
監視ビデオ解像度:
私たちは、オブジェクトをAVCaptureSessionすることができますsessionPreset
し、我々は、印刷設定が上に完全なクリックで直接、それは文字列であり、直接に。
監視ビデオのフレームレート:
ビデオフレームレートが第二の映像ごとにキャプチャされたフレームの数を表し、我々は現在のリアルタイムビデオキャプチャフレームレートが印刷し、(1Sリフレッシュ)、タイマーを開始することができます。以下は、コンピューティング1Sコード内のビデオフレームのコレクションです。
// 计算每秒钟采集视频多少帧
static int captureVideoFPS;
+ (void)calculatorCaptureFPS
{
static int count = 0;
static float lastTime = 0;
CMClockRef hostClockRef = CMClockGetHostTimeClock();
CMTime hostTime = CMClockGetTime(hostClockRef);
float nowTime = CMTimeGetSeconds(hostTime);
if(nowTime - lastTime >= 1)
{
captureVideoFPS = count;
lastTime = nowTime;
count = 0;
}
else
{
count ++;
}
}
// 获取视频帧率
+ (int)getCaptureVideoFPS
{
return captureVideoFPS;
}
解像度を変更します
/**
* Reset resolution
*
* @param m_session AVCaptureSession instance
* @param resolution
*/
+ (void)resetSessionPreset:(AVCaptureSession *)m_session resolution:(int)resolution
{
[m_session beginConfiguration];
switch (resolution) {
case 1080:
m_session.sessionPreset = [m_session canSetSessionPreset:AVCaptureSessionPreset1920x1080] ? AVCaptureSessionPreset1920x1080 : AVCaptureSessionPresetHigh;
break;
case 720:
m_session.sessionPreset = [m_session canSetSessionPreset:AVCaptureSessionPreset1280x720] ? AVCaptureSessionPreset1280x720 : AVCaptureSessionPresetMedium;
break;
case 480:
m_session.sessionPreset = [m_session canSetSessionPreset:AVCaptureSessionPreset640x480] ? AVCaptureSessionPreset640x480 : AVCaptureSessionPresetMedium;
break;
case 360:
m_session.sessionPreset = AVCaptureSessionPresetMedium;
break;
default:
break;
}
[m_session commitConfiguration];
}
変更ビデオフレームレート
+ (void)settingFrameRate:(int)frameRate
{
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[captureDevice lockForConfiguration:NULL];
@try {
[captureDevice setActiveVideoMinFrameDuration:CMTimeMake(1, frameRate)];
[captureDevice setActiveVideoMaxFrameDuration:CMTimeMake(1, frameRate)];
} @catch (NSException *exception) {
NSLog(@"MediaIOS, 设备不支持所设置的分辨率,错误信息:%@",exception.description);
} @finally {
}
[captureDevice unlockForConfiguration];
}
ピンチジェスチャー層のビデオプレビューを追加
とき潜在的な両手で、ビデオプレビューをズームすることができます。
#define MiMaxZoomFactor 5.0f
#define MiPrinchVelocityDividerFactor 20.0f
+ (void)zoomCapture:(UIPinchGestureRecognizer *)recognizer
{
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[videoDevice formats];
if ([recognizer state] == UIGestureRecognizerStateChanged) {
NSError *error = nil;
if ([videoDevice lockForConfiguration:&error]) {
CGFloat desiredZoomFactor = videoDevice.videoZoomFactor + atan2f(recognizer.velocity, MiPrinchVelocityDividerFactor);
videoDevice.videoZoomFactor = desiredZoomFactor <= MiMaxZoomFactor ? MAX(1.0, MIN(desiredZoomFactor, videoDevice.activeFormat.videoMaxZoomFactor)) : MiMaxZoomFactor ;
[videoDevice unlockForConfiguration];
} else {
NSLog(@"error: %@", error);
}
}
}
カメラの操作
ビデオキャプチャは、おそらくレンズ、オープン&クローズフラッシュ、カメラや他の操作を中心に切り替えを伴うとき。
前後にレンズカメラの切り替え
ここでレンズを切り替えた後、いくつかのデバイス用のフロントカメラ1080をサポートしていない可能性があるため、私は、解像度720pのためにデフォルト設定を置くので、私は実際のプロジェクトでは、この値はする必要がある場合は、固定720Pを設定するためにここにいますフロントカメラは、対応するサポートポリシーをサポートしていない場合は、以前に、値を設定しています。
// 切换摄像头
- (void)switchCamera
{
[_m_session beginConfiguration];
if ([[_video_input device] position] == AVCaptureDevicePositionBack) {
NSArray * devices = [AVCaptureDevice devices];
for(AVCaptureDevice * device in devices) {
if([device hasMediaType:AVMediaTypeVideo]) {
if([device position] == AVCaptureDevicePositionFront) {
[self rePreviewWithCameraType:MiCameraType_Front device:device];
break;
}
}
}
}else{
NSArray * devices = [AVCaptureDevice devices];
for(AVCaptureDevice * device in devices) {
if([device hasMediaType:AVMediaTypeVideo]) {
if([device position] == AVCaptureDevicePositionBack) {
[self rePreviewWithCameraType:MiCameraType_Back device:device];
break;
}
}
}
}
[_m_session commitConfiguration];
}
- (void)rePreviewWithCameraType:(MiCameraType)cameraType device:(AVCaptureDevice *)device {
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device
error:&error];
if (!input) return;
[_m_session removeInput:_video_input];
_m_session.sessionPreset = AVCaptureSessionPresetLow;
if ([_m_session canAddInput:input]) {
[_m_session addInput:input];
}else {
return;
}
_video_input = input;
_m_cameraType = cameraType;
NSString *preset = AVCaptureSessionPreset1280x720;
if([device supportsAVCaptureSessionPreset:preset] && [_m_session canSetSessionPreset:preset]) {
_m_session.sessionPreset = preset;
}else {
NSString *sesssionPreset = AVCaptureSessionPreset1280x720;
if(![sesssionPreset isEqualToString:preset]) {
_m_session.sessionPreset = sesssionPreset;
}
}
}
フラッシュをオフに開きます
// 打开关闭闪光灯
-(void)switchTorch
{
[_m_session beginConfiguration];
[[_video_input device] lockForConfiguration:NULL];
self.m_torchMode = [_video_input device].torchMode == AVCaptureTorchModeOn ? AVCaptureTorchModeOff : AVCaptureTorchModeOn;
if ([[_video_input device] isTorchModeSupported:_m_torchMode ]) {
[_video_input device].torchMode = self.m_torchMode;
}
[[_video_input device] unlockForConfiguration];
[_m_session commitConfiguration];
}
写真を撮ると、アルバムに保存
特定のプログラムは以下のとおりです。
- カメラフラグの値を変更するアクションをトリガされたとき、フラグ、ビデオキャプチャを監視するプロキシ方式にフラグを設定します
- 現在のフレームが変換された場合、ビデオ捕捉剤の方法は、充填物の写真を撮影する必要があるかどうかのフラグの値を決定
CMSampleBufferRef
UIImageに、その後アルバムUIImageに格納されています
注:次のコードを場合にのみ指定されたピクセルRGBフォーマット、正常アルバムにカラー写真を保存するために。
- (UIImage *)convertSameBufferToUIImage:(CMSampleBufferRef)sampleBuffer
{
// 为媒体数据设置一个CMSampleBuffer的Core Video图像缓存对象
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// 锁定pixel buffer的基地址
CVPixelBufferLockBaseAddress(imageBuffer, 0);
// 得到pixel buffer的基地址
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
// 得到pixel buffer的行字节数
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// 得到pixel buffer的宽和高
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// 创建一个依赖于设备的RGB颜色空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// 用抽样缓存的数据创建一个位图格式的图形上下文(graphics context)对象
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8,bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
// 根据这个位图context中的像素数据创建一个Quartz image对象
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
// 解锁pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
// 释放context和颜色空间
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
// 用Quartz image创建一个UIImage对象image
UIImage *image = [UIImage imageWithCGImage:quartzImage];
// 释放Quartz image对象
CGImageRelease(quartzImage);
return (image);
}
+ (void)saveImageToSysphotos:(UIImage *)image
{
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:image.CGImage metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog(@"MediaIos, save photo to photos error, error info: %@",error.description);
}else{
NSLog(@"MediaIos, save photo success...");
}
}];
}
AFの設定
// 设置为自动对焦
- (void)mifocus:(UITapGestureRecognizer *)sender
{
CGPoint point = [sender locationInView:self.m_displayView];
[self miAutoFocusWithPoint:point];
NSLog(@"MediaIos, auto focus complete...");
}
- (void)miAutoFocusWithPoint:(CGPoint)touchPoint{
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([captureDevice isFocusPointOfInterestSupported] && [captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
NSError *error;
if ([captureDevice lockForConfiguration:&error]) {
// 设置曝光点
[captureDevice setExposurePointOfInterest:touchPoint];
[captureDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
// 设置对焦点
[captureDevice setFocusPointOfInterest:touchPoint];
[captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
[captureDevice unlockForConfiguration];
}
}
}
露出調整
// 曝光调节
- (void)changeExposure:(id)sender
{
UISlider *slider = (UISlider *)sender;
[self michangeExposure:slider.value];
}
- (void)michangeExposure:(CGFloat)value{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error;
if ([device lockForConfiguration:&error]) {
[device setExposureTargetBias:value completionHandler:nil];
[device unlockForConfiguration];
}
}
ホワイトバランス設定
- (AVCaptureWhiteBalanceGains)recalcGains:(AVCaptureWhiteBalanceGains)gains
minValue:(CGFloat)minValue
maxValue:(CGFloat)maxValue
{
AVCaptureWhiteBalanceGains tmpGains = gains;
tmpGains.blueGain = MAX(MIN(tmpGains.blueGain , maxValue), minValue);
tmpGains.redGain = MAX(MIN(tmpGains.redGain , maxValue), minValue);
tmpGains.greenGain = MAX(MIN(tmpGains.greenGain, maxValue), minValue);
return tmpGains;
}
-(void)setWhiteBlanceUseTemperature:(CGFloat)temperature{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) {
[device lockForConfiguration:nil];
AVCaptureWhiteBalanceGains currentGains = device.deviceWhiteBalanceGains;
CGFloat currentTint = [device temperatureAndTintValuesForDeviceWhiteBalanceGains:currentGains].tint;
AVCaptureWhiteBalanceTemperatureAndTintValues tempAndTintValues = {
.temperature = temperature,
.tint = currentTint,
};
AVCaptureWhiteBalanceGains gains = [device deviceWhiteBalanceGainsForTemperatureAndTintValues:tempAndTintValues];
CGFloat maxWhiteBalanceGain = device.maxWhiteBalanceGain;
gains = [self recalcGains:gains minValue:1 maxValue:maxWhiteBalanceGain];
[device setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:gains completionHandler:nil];
[device unlockForConfiguration];
}
}
// 黑白平衡调节
- (void)whiteBlanceChange:(id)sender
{
UISlider *slider = (UISlider *)sender;
[self setWhiteBlanceUseTemperature:slider.value];
}
ます。https://www.jianshu.com/p/a0e2d7b3b8a7で再現