该demo以ToyCamera命名
常用类、方法、属性已在【iOS视频捕获入门篇】介绍。
文章目录
前面提到我们不能直接设备,而是需要用一个输入对象将设备封装。先来提供一个前置摄像头和一个后置摄像头。(后置摄像头我们只使用一个)
- (AVCaptureDevice *)videoFrontDevice {
if (!_captureDevice || self.captureDevice.position != AVCaptureDevicePositionFront) {
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInDualCamera, AVCaptureDeviceTypeBuiltInTelephotoCamera,AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionFront];
_captureDevice = session.devices.firstObject;
}
return _captureDevice;
}
- (AVCaptureDevice *)videoBackDevice {
if (!_captureDevice || self.captureDevice.position != AVCaptureDevicePositionBack) {
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInDualCamera, AVCaptureDeviceTypeBuiltInTelephotoCamera,AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
_captureDevice = session.devices.firstObject;
}
return _captureDevice;
}
通过Input对象来封装设备:
- (void)_addCaptureIntput {
[self.metadataDevice lockForConfiguration:nil];
AVCaptureDeviceInput *deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.videoBackDevice error:nil];
if ([self.captureSession canAddInput:deviceInput]) {
[self.captureSession addInput:deviceInput];
}
[self.metadataDevice unlockForConfiguration];
}
先实现视频流,定义一个视频流输出:
@property (nonatomic) AVCaptureVideoDataOutput *captureVideoDataOutput;
_captureVideoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
// delegate可以实现 AVCaptureVideoDataOutputSampleBufferDelegate 中的方法来接收视频帧
[self.captureVideoDataOutput setSampleBufferDelegate:self.delegate queue:self.sampleBufferQueue];
self.captureVideoDataOutput.videoSettings = @{(__bridge NSString *)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithInt:kCVPixelFormatType_32BGRA]};
最重要的就是会话。
_captureSession = [[AVCaptureSession alloc] init];
// 开始配置
[self.captureSession beginConfiguration];
// 设置质量
self.captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
// 添加输入
[self.captureDevice lockForConfiguration:nil];
AVCaptureDeviceInput *deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.videoBackDevice error:nil];
if ([self.captureSession canAddInput:deviceInput]) {
[self.captureSession addInput:deviceInput];
}
[self.captureDevice unlockForConfiguration];
// 添加输出
if ([self.captureSession canAddOutput:self.captureVideoDataOutput]) {
[self.captureSession addOutput:self.captureVideoDataOutput];
}
[self.captureSession commitConfiguration];
前面的准备工作做完之后,就可以添加预览页面,这样才能看到我们捕获的视频。
// 就是一个Layer,返回后添加到合适的View上即可。
- (AVCaptureVideoPreviewLayer *)videoPreviewLayer {
if (!_videoPreviewLayer) {
_videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
_videoPreviewLayer.videoGravity= AVLayerVideoGravityResizeAspectFill;
_videoPreviewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait;
}
return _videoPreviewLayer;
}
至此其实就有一个可以看的见的视频流了。但是仅仅看到不是我们所希望的,我们需要拍照、录视频等。当然对于使用视频流做追踪、处理是够了。而且使用视频输出的视频帧也可以转为图片从而实现拍照。
输入我们可以共用,对于正式的拍照,我们需要使用AVCapturePhotoOutput实现。
_capturePhotoOutput = [[AVCapturePhotoOutput alloc] init];
[self.captureSession addOutput:self.capturePhotoOutput];
NSMutableArray<AVVideoCodecType> *availablePhotoCodecTypes = [NSMutableArray array];
AVCapturePhotoSettings *settings;
for (AVVideoCodecType type in self.capturePhotoOutput.availablePhotoCodecTypes) {
if ([type isEqualToString:AVVideoCodecTypeJPEG]) {
settings = [AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey: AVVideoCodecTypeJPEG}];
[availablePhotoCodecTypes addObject:AVVideoCodecTypeJPEG];
break;
}
}
// 这里的原因在【iOS视频捕获入门篇】已介绍
if (settings) {
[self.capturePhotoOutput setPreparedPhotoSettingsArray:@[settings] completionHandler:^(BOOL prepared, NSError * _Nullable error) {
if (prepared) {
} else if (error) {
}
}];
}
通过下面的api获得照片。每次获取照片时,我们都需要传入一个AVCapturePhotoSettings。
// delegate需要实现 AVCapturePhotoCaptureDelegate 协议
[self.capturePhotoOutput capturePhotoWithSettings:[AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey: AVVideoCodecTypeJPEG}] delegate:self.delegate];
// 实现 AVCapturePhotoCaptureDelegate 协议
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(nullable NSError *)error {
if (!error) {
NSData *data = [photo fileDataRepresentation];
UIImage *image = [UIImage imageWithData:data];
// 保存照片
[TCUtility saveImage:image];
}
}
这里录制视频没有录制音频,只有视频画面。
录制视频我们需要使用到 AVCaptureMovieFileOutput。其属性和方法在 【iOS视频捕获入门篇】已介绍
_captureMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
// 简单点,其他参数使用默认的
if ([self.captureSession canAddOutput:self.captureMovieFileOutput]) {
[self.captureSession addOutput:self.captureMovieFileOutput];
}
我们使用一个方法就可以实现开始录制和停止录制
- (void)captureVideo {
// 如果是录制中,那么本次调用就是停止录制
if (self.captureMovieFileOutput.isRecording) {
[self.captureMovieFileOutput stopRecording];
}
// 否则就是开始录制
NSString *urlString = [NSTemporaryDirectory() stringByAppendingString:[NSString stringWithFormat:@"%.0f.mov", [[NSDate date] timeIntervalSince1970] * 1000]];
NSURL *url = [NSURL fileURLWithPath:urlString];
// 代理
[self.captureMovieFileOutput startRecordingToOutputFileURL:url recordingDelegate:self.delegate];
}
// 实现代理
- (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray<AVCaptureConnection *> *)connections error:(nullable NSError *)error {
// 可以将URL存入到相册
[TCUtility saveVideo:outputFileURL];
}
整体一个简单的相机就实现了。这里只是介绍了我们怎么来使用前面介绍的api,如果有需要更高级的相机,还需要去深入挖掘学习AVFoundation中媒体捕获的内容。
还是比较简单的,整个demo我会放在GitHub上,有兴趣的同学可以去拉下来跑一跑。
最后一篇文章就是【iOS视频捕获进阶篇】,主要介绍怎么使用AVCaptureMetadataOutput。我们可以实现人脸检测与追踪。
瑞思拜~
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、
2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
我正在尝试使用ruby编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?
我在使用自定义RailsFormBuilder时遇到了问题,从昨天晚上开始我就发疯了。基本上我想对我的构建器方法之一有一个可选block,以便我可以在我的主要content_tag中显示其他内容。:defform_field(method,&block)content_tag(:div,class:'field')doconcatlabel(method,"Label#{method}")concattext_field(method)capture(&block)ifblock_given?endend当我在我的一个Slim模板中调用该方法时,如下所示:=f.form_field:e