当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS使用视听媒体框架AVFoundation实现照片拍摄

iOS使用视听媒体框架AVFoundation实现照片拍摄

2019年07月23日  | 移动技术网IT编程  | 我要评论

凡妮莎海辛,ume安贞,巴中市人力资源和社会保障局

用系统自带的视听媒体的框架,avfoundation实现照片拍摄。相比uikit框架(uiimagepickercontroller高度封装),avfoundation框架让开发者有更大的发挥空间。

首先看一下效果图:

下面贴上核心控制器代码:

#import "hwphotovc.h"
#import <avfoundation/avfoundation.h>
 
@interface hwphotovc ()
 
@property (nonatomic, strong) avcapturesession *capturesession;//负责输入和输出设备之间的数据传递
@property (nonatomic, strong) avcapturedeviceinput *capturedeviceinput;//负责从avcapturedevice获得输入数据
@property (nonatomic, strong) avcapturestillimageoutput *capturestillimageoutput;//照片输出流
@property (nonatomic, strong) avcapturevideopreviewlayer *capturevideopreviewlayer;//相机拍摄预览图层
@property (nonatomic, weak) uiview *containerview;//内容视图
@property (nonatomic, weak) uiimageview *focuscursor;//聚焦按钮
@property (nonatomic, weak) uiimageview *imgview;//拍摄照片
 
@end
 
@implementation hwphotovc
 
- (void)viewdidload {
 [super viewdidload];
 
 self.navigationitem.title = @"拍照";
 self.view.backgroundcolor = [uicolor whitecolor];
 
 //创建控件
 [self creatcontrol];
}
 
- (void)viewwillappear:(bool)animated
{
 [super viewwillappear:animated];
 
 //初始化信息
 [self initphotoinfo];
}
 
- (void)viewdidappear:(bool)animated
{
 [super viewdidappear:animated];
 
 [self.capturesession startrunning];
}
 
- (void)viewdiddisappear:(bool)animated
{
 [super viewdiddisappear:animated];
 
 [self.capturesession stoprunning];
}
 
- (void)creatcontrol
{
 cgfloat btnw = 150.f;
 cgfloat btnh = 40.f;
 cgfloat marginy = 20.f;
 cgfloat w = [uiscreen mainscreen].bounds.size.width;
 cgfloat h = [uiscreen mainscreen].bounds.size.height;
 
 //内容视图
 cgfloat containerviewh = h - 64 - btnh - marginy * 3;
 uiview *containerview = [[uiview alloc] initwithframe:cgrectmake(10, 64 + marginy, w - 20, containerviewh)];
 containerview.backgroundcolor = [uicolor whitecolor];
 containerview.layer.borderwidth = 1.f;
 containerview.layer.bordercolor = [[uicolor graycolor] cgcolor];
 [self.view addsubview:containerview];
 _containerview = containerview;
 
 //摄像头切换按钮
 cgfloat cameraswitchbtnw = 50.f;
 cgfloat cameraswitchbtnmargin = 10.f;
 uibutton *cameraswitchbtn = [[uibutton alloc] initwithframe:cgrectmake(containerview.bounds.size.width - cameraswitchbtnw - cameraswitchbtnmargin, 64 + marginy + cameraswitchbtnmargin, cameraswitchbtnw, cameraswitchbtnw)];
 [cameraswitchbtn setimage:[uiimage imagenamed:@"camera_switch"] forstate:uicontrolstatenormal];
 [cameraswitchbtn addtarget:self action:@selector(cameraswitchbtnonclick) forcontrolevents:uicontroleventtouchupinside];
 [self.view addsubview:cameraswitchbtn];
 
 //聚焦图片
 uiimageview *focuscursor = [[uiimageview alloc] initwithframe:cgrectmake(50, 50, 75, 75)];
 focuscursor.alpha = 0;
 focuscursor.image = [uiimage imagenamed:@"camera_focus_red"];
 [containerview addsubview:focuscursor];
 _focuscursor = focuscursor;
 
 //拍摄照片容器
 uiimageview *imgview = [[uiimageview alloc] initwithframe:containerview.frame];
 imgview.hidden = yes;
 imgview.layer.borderwidth = 1.f;
 imgview.layer.bordercolor = [[uicolor graycolor] cgcolor];
 imgview.contentmode = uiviewcontentmodescaleaspectfill;
 imgview.clipstobounds = yes;
 [self.view addsubview:imgview];
 _imgview = imgview;
 
 //按钮
 nsarray *titlearray = @[@"拍摄照片", @"重新拍摄"];
 cgfloat btny = cgrectgetmaxy(containerview.frame) + marginy;
 cgfloat margin = (w - btnw * titlearray.count) / (titlearray.count + 1);
 for (int i = 0; i < titlearray.count; i++) {
  cgfloat btnx = margin + (margin + btnw) * i;
  uibutton *btn = [[uibutton alloc] initwithframe:cgrectmake(btnx, btny, btnw, btnh)];
  btn.tag = 1000 + i;
  [btn settitle:titlearray[i] forstate:uicontrolstatenormal];
  btn.backgroundcolor = [uicolor orangecolor];
  btn.layer.cornerradius = 2.0f;
  btn.layer.maskstobounds = yes;
  if (i == 1) {
   btn.hidden = yes;
  }
  [btn addtarget:self action:@selector(btnonclick:) forcontrolevents:uicontroleventtouchupinside];
  [self.view addsubview:btn];
 }
}
 
- (void)initphotoinfo
{
 //初始化会话
 _capturesession = [[avcapturesession alloc] init];
 
 //设置分辨率
 if ([_capturesession cansetsessionpreset:avcapturesessionpreset1280x720]) {
  _capturesession.sessionpreset = avcapturesessionpreset1280x720;
 }
 
 //获得输入设备,取得后置摄像头
 avcapturedevice *capturedevice = [self getcameradevicewithposition:avcapturedevicepositionback];
 if (!capturedevice) {
  nslog(@"取得后置摄像头时出现问题");
  return;
 }
 
 nserror *error = nil;
 //根据输入设备初始化设备输入对象,用于获得输入数据
 _capturedeviceinput = [[avcapturedeviceinput alloc]initwithdevice:capturedevice error:&error];
 if (error) {
  nslog(@"取得设备输入对象时出错,错误原因:%@", error.localizeddescription);
  return;
 }
 
 //初始化设备输出对象,用于获得输出数据
 _capturestillimageoutput = [[avcapturestillimageoutput alloc] init];
 nsdictionary *outputsettings = @{avvideocodeckey:avvideocodecjpeg};
 //输出设置
 [_capturestillimageoutput setoutputsettings:outputsettings];
 
 //将设备输入添加到会话中
 if ([_capturesession canaddinput:_capturedeviceinput]) {
  [_capturesession addinput:_capturedeviceinput];
 }
 
 //将设备输出添加到会话中
 if ([_capturesession canaddoutput:_capturestillimageoutput]) {
  [_capturesession addoutput:_capturestillimageoutput];
 }
 
 //创建视频预览层,用于实时展示摄像头状态
 _capturevideopreviewlayer = [[avcapturevideopreviewlayer alloc] initwithsession:self.capturesession];
 
 //摄像头方向
 avcaptureconnection *captureconnection = [self.capturevideopreviewlayer connection];
 captureconnection.videoorientation = avcapturevideoorientationportrait;
 
 calayer *layer = _containerview.layer;
 layer.maskstobounds = yes;
 
 _capturevideopreviewlayer.frame = layer.bounds;
 //填充模式
 _capturevideopreviewlayer.videogravity = avlayervideogravityresizeaspectfill;
 //将视频预览层添加到界面中
 [layer insertsublayer:_capturevideopreviewlayer below:self.focuscursor.layer];
 
 [self addnotificationtocapturedevice:capturedevice];
 [self addgensturerecognizer];
}
 
- (void)btnonclick:(uibutton *)btn
{
 if (btn.tag == 1000) {
  //拍摄照片
  [self photobtnonclick];
  
 }else if (btn.tag == 1001) {
  //重新拍摄
  [self resetphoto];
 }
}
 
#pragma mark 拍照
- (void)photobtnonclick
{
 //根据设备输出获得连接
 avcaptureconnection *captureconnection = [self.capturestillimageoutput connectionwithmediatype:avmediatypevideo];
 captureconnection.videoorientation = avcapturevideoorientationportrait;
 
 //根据连接取得设备输出的数据
 [self.capturestillimageoutput capturestillimageasynchronouslyfromconnection:captureconnection completionhandler:^(cmsamplebufferref imagedatasamplebuffer, nserror *error) {
  if (imagedatasamplebuffer) {
   nsdata *imagedata = [avcapturestillimageoutput jpegstillimagensdatarepresentation:imagedatasamplebuffer];
   uiimage *image = [uiimage imagewithdata:imagedata];
   _imgview.image = image;
   _imgview.hidden = no;
  }
 }];
 
 uibutton *btn = (uibutton *)[self.view viewwithtag:1001];
 btn.hidden = no;
}
 
//重新拍摄
- (void)resetphoto
{
 _imgview.hidden = yes;
 uibutton *btn = (uibutton *)[self.view viewwithtag:1001];
 btn.hidden = yes;
}
 
#pragma mark - 通知
//给输入设备添加通知
- (void)addnotificationtocapturedevice:(avcapturedevice *)capturedevice
{
 //注意添加区域改变捕获通知必须首先设置设备允许捕获
 [self changedeviceproperty:^(avcapturedevice *capturedevice) {
  capturedevice.subjectareachangemonitoringenabled = yes;
 }];
 
 //捕获区域发生改变
 [[nsnotificationcenter defaultcenter] addobserver:self selector:@selector(areachange:) name:avcapturedevicesubjectareadidchangenotification object:capturedevice];
}
 
- (void)removenotificationfromcapturedevice:(avcapturedevice *)capturedevice
{
 [[nsnotificationcenter defaultcenter] removeobserver:self name:avcapturedevicesubjectareadidchangenotification object:capturedevice];
}
 
//移除所有通知
- (void)removenotification
{
 [[nsnotificationcenter defaultcenter] removeobserver:self];
}
 
//设备连接成功
- (void)deviceconnected:(nsnotification *)notification
{
 nslog(@"设备已连接...");
}
 
//设备连接断开
- (void)devicedisconnected:(nsnotification *)notification
{
 nslog(@"设备已断开.");
}
 
//捕获区域改变
- (void)areachange:(nsnotification *)notification
{
 nslog(@"捕获区域改变...");
}
 
#pragma mark - 私有方法
//取得指定位置的摄像头
- (avcapturedevice *)getcameradevicewithposition:(avcapturedeviceposition )position
{
 nsarray *cameras = [avcapturedevice deviceswithmediatype:avmediatypevideo];
 for (avcapturedevice *camera in cameras) {
  if ([camera position] == position) {
   return camera;
  }
 }
 
 return nil;
}
 
#pragma mark 切换前后摄像头
- (void)cameraswitchbtnonclick
{
 avcapturedevice *currentdevice = [self.capturedeviceinput device];
 avcapturedeviceposition currentposition = [currentdevice position];
 [self removenotificationfromcapturedevice:currentdevice];
 
 avcapturedevice *tochangedevice;
 avcapturedeviceposition tochangeposition = avcapturedevicepositionfront;
 if (currentposition == avcapturedevicepositionunspecified || currentposition == avcapturedevicepositionfront) {
  tochangeposition = avcapturedevicepositionback;
 }
 tochangedevice = [self getcameradevicewithposition:tochangeposition];
 [self addnotificationtocapturedevice:tochangedevice];
 //获得要调整的设备输入对象
 avcapturedeviceinput *tochangedeviceinput = [[avcapturedeviceinput alloc] initwithdevice:tochangedevice error:nil];
 
 //改变会话的配置前一定要先开启配置,配置完成后提交配置改变
 [self.capturesession beginconfiguration];
 //移除原有输入对象
 [self.capturesession removeinput:self.capturedeviceinput];
 //添加新的输入对象
 if ([self.capturesession canaddinput:tochangedeviceinput]) {
  [self.capturesession addinput:tochangedeviceinput];
  self.capturedeviceinput = tochangedeviceinput;
 }
 //提交会话配置
 [self.capturesession commitconfiguration];
}
 
//改变设备属性的统一操作方法
- (void)changedeviceproperty:(void (^)(avcapturedevice *))propertychange
{
 avcapturedevice *capturedevice = [self.capturedeviceinput device];
 nserror *error;
 //注意改变设备属性前一定要首先调用lockforconfiguration:调用完之后使用unlockforconfiguration方法解锁
 if ([capturedevice lockforconfiguration:&error]) {
  propertychange(capturedevice);
  [capturedevice unlockforconfiguration];
  
 }else {
  nslog(@"设置设备属性过程发生错误,错误信息:%@", error.localizeddescription);
 }
}
 
//设置闪光灯模式
- (void)setflashmode:(avcaptureflashmode)flashmode
{
 [self changedeviceproperty:^(avcapturedevice *capturedevice) {
  if ([capturedevice isflashmodesupported:flashmode]) {
   [capturedevice setflashmode:flashmode];
  }
 }];
}
 
//设置聚焦模式
- (void)setfocusmode:(avcapturefocusmode)focusmode
{
 [self changedeviceproperty:^(avcapturedevice *capturedevice) {
  if ([capturedevice isfocusmodesupported:focusmode]) {
   [capturedevice setfocusmode:focusmode];
  }
 }];
}
 
//设置曝光模式
- (void)setexposuremode:(avcaptureexposuremode)exposuremode
{
 [self changedeviceproperty:^(avcapturedevice *capturedevice) {
  if ([capturedevice isexposuremodesupported:exposuremode]) {
   [capturedevice setexposuremode:exposuremode];
  }
 }];
}
 
//设置聚焦点
- (void)focuswithmode:(avcapturefocusmode)focusmode exposuremode:(avcaptureexposuremode)exposuremode atpoint:(cgpoint)point
{
 [self changedeviceproperty:^(avcapturedevice *capturedevice) {
  if ([capturedevice isfocusmodesupported:focusmode]) {
   [capturedevice setfocusmode:avcapturefocusmodeautofocus];
  }
  if ([capturedevice isfocuspointofinterestsupported]) {
   [capturedevice setfocuspointofinterest:point];
  }
  if ([capturedevice isexposuremodesupported:exposuremode]) {
   [capturedevice setexposuremode:avcaptureexposuremodeautoexpose];
  }
  if ([capturedevice isexposurepointofinterestsupported]) {
   [capturedevice setexposurepointofinterest:point];
  }
 }];
}
 
//添加点按手势,点按时聚焦
- (void)addgensturerecognizer
{
 [self.containerview addgesturerecognizer:[[uitapgesturerecognizer alloc] initwithtarget:self action:@selector(tapscreen:)]];
}
 
- (void)tapscreen:(uitapgesturerecognizer *)tapgesture
{
 cgpoint point = [tapgesture locationinview:self.containerview];
 //将ui坐标转化为摄像头坐标
 cgpoint camerapoint = [self.capturevideopreviewlayer capturedevicepointofinterestforpoint:point];
 [self setfocuscursorwithpoint:point];
 [self focuswithmode:avcapturefocusmodeautofocus exposuremode:avcaptureexposuremodeautoexpose atpoint:camerapoint];
}
 
//设置聚焦光标位置
- (void)setfocuscursorwithpoint:(cgpoint)point
{
 self.focuscursor.center = point;
 self.focuscursor.transform = cgaffinetransformmakescale(1.5, 1.5);
 self.focuscursor.alpha = 1.0;
 [uiview animatewithduration:1.0 animations:^{
  self.focuscursor.transform = cgaffinetransformidentity;
 } completion:^(bool finished) {
  self.focuscursor.alpha = 0;
 }];
}
 
- (void)dealloc
{
 [self removenotification];
}
 
@end

demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网