RScanCamera.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. //
  2. // RScanCamera.m
  3. // r_scan
  4. //
  5. // Created by 李鹏辉 on 2020/2/24.
  6. //
  7. #import "RScanCamera.h"
  8. #import <AVFoundation/AVFoundation.h>
  9. #import <CoreMotion/CoreMotion.h>
  10. #import <libkern/OSAtomic.h>
  11. #import "RScanResult.h"
  12. #define ScanY 50 //扫描区域y
  13. #define ScanWidth 250 //扫描区域宽度
  14. #define ScanHeight 500 //扫描区域高度
  15. /* 屏幕宽 */
  16. #define LWSCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
  17. /* 屏幕高 */
  18. #define LWSCREENH_HEIGHT [UIScreen mainScreen].bounds.size.height
  19. // Mirrors ResolutionPreset in camera.dart
  20. typedef enum {
  21. veryLow,
  22. low,
  23. medium,
  24. high,
  25. veryHigh,
  26. ultraHigh,
  27. max,
  28. } ResolutionPreset;
  29. static ResolutionPreset getResolutionPresetForString(NSString *preset) {
  30. if ([preset isEqualToString:@"veryLow"]) {
  31. return veryLow;
  32. } else if ([preset isEqualToString:@"low"]) {
  33. return low;
  34. } else if ([preset isEqualToString:@"medium"]) {
  35. return medium;
  36. } else if ([preset isEqualToString:@"high"]) {
  37. return high;
  38. } else if ([preset isEqualToString:@"veryHigh"]) {
  39. return veryHigh;
  40. } else if ([preset isEqualToString:@"ultraHigh"]) {
  41. return ultraHigh;
  42. } else if ([preset isEqualToString:@"max"]) {
  43. return max;
  44. } else {
  45. NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
  46. code:NSURLErrorUnknown
  47. userInfo:@{
  48. NSLocalizedDescriptionKey : [NSString
  49. stringWithFormat:@"Unknown resolution preset %@", preset]
  50. }];
  51. @throw error;
  52. }
  53. }
  54. //获取错误
  55. static FlutterError *getFlutterError(NSError *error) {
  56. return [FlutterError errorWithCode:[NSString stringWithFormat:@"%d", (int)error.code]
  57. message:error.localizedDescription
  58. details:error.domain];
  59. }
  60. @interface RScanFLTCam : NSObject<FlutterTexture,AVCaptureMetadataOutputObjectsDelegate,AVCaptureVideoDataOutputSampleBufferDelegate,FlutterStreamHandler>
  61. @property(readonly, nonatomic) int64_t textureId;
  62. @property(assign, nonatomic) ResolutionPreset resolutionPreset;
  63. //链接相机用的
  64. @property(readonly, nonatomic) AVCaptureSession *captureSession;
  65. //获取相机设备
  66. @property(readonly, nonatomic) AVCaptureDevice *captureDevice;
  67. //视频输入
  68. @property(readonly, nonatomic) AVCaptureInput *captureVideoInput;
  69. //视频输出
  70. @property(readonly, nonatomic) AVCaptureMetadataOutput * captureOutput;
  71. //视频输出2
  72. @property(readonly, nonatomic) AVCaptureVideoDataOutput *captureVideoOutput;
  73. @property(readonly, nonatomic) CGSize previewSize;
  74. @property(nonatomic) FlutterEventSink eventSink;
  75. @property(readonly) CVPixelBufferRef volatile latestPixelBuffer;
  76. //第一帧回掉
  77. @property(nonatomic, copy) void (^onFrameAvailable)(void);
  78. //channel用于返回数据给data
  79. @property(nonatomic) FlutterEventChannel *eventChannel;
  80. @end
  81. @implementation RScanFLTCam{
  82. dispatch_queue_t _dispatchQueue;
  83. }
  84. FourCharCode const rScanVideoFormat = kCVPixelFormatType_32BGRA;
  85. - (instancetype)initWitchCameraName:(NSString*)cameraName resolutionPreset:(NSString*)resolutionPreset dispatchQueue:(dispatch_queue_t)dispatchQueue error:(NSError **)error{
  86. self = [super init];
  87. NSAssert(self, @"super init cannot be nil");
  88. @try {
  89. _resolutionPreset =getResolutionPresetForString(resolutionPreset);
  90. } @catch (NSError *e) {
  91. *error = e;
  92. }
  93. _dispatchQueue =dispatchQueue;
  94. _captureSession=[[AVCaptureSession alloc]init];
  95. _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName];
  96. NSError *localError =nil;
  97. _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice error:&localError];
  98. if(localError){
  99. *error = localError;
  100. return nil;
  101. }
  102. _captureVideoOutput = [AVCaptureVideoDataOutput new];
  103. _captureVideoOutput.videoSettings = @{(NSString*)kCVPixelBufferPixelFormatTypeKey: @(rScanVideoFormat)};
  104. [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES];
  105. [_captureVideoOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
  106. //链接
  107. AVCaptureConnection* connection =[AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports output:_captureVideoOutput];
  108. if ([_captureDevice position] == AVCaptureDevicePositionFront) {
  109. connection.videoMirrored = YES;
  110. }
  111. if([connection isVideoOrientationSupported]){
  112. connection.videoOrientation =AVCaptureVideoOrientationPortrait;
  113. }
  114. [_captureSession addInputWithNoConnections:_captureVideoInput];
  115. [_captureSession addOutputWithNoConnections:_captureVideoOutput];
  116. _captureOutput=[[AVCaptureMetadataOutput alloc]init];
  117. //设置代理,在主线程刷新
  118. [_captureOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
  119. [_captureSession addOutput:_captureOutput];
  120. _captureOutput.metadataObjectTypes=_captureOutput.availableMetadataObjectTypes;
  121. //扫码区域的大小
  122. AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:_captureSession];
  123. layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
  124. layer.frame = CGRectMake(0, 0, LWSCREEN_WIDTH, LWSCREENH_HEIGHT);
  125. // [_captureOutput rectOfInterest];
  126. // _captureOutput.rectOfInterest = CGRectMake(0.5,0.5,0.5,0.5);
  127. CGFloat top = (LWSCREENH_HEIGHT - LWSCREEN_WIDTH *2 /3)/3.2;
  128. CGSize size = CGSizeMake(LWSCREEN_WIDTH, LWSCREENH_HEIGHT);
  129. CGRect cropRect = CGRectMake(LWSCREEN_WIDTH/6, top, LWSCREEN_WIDTH *2 /3, LWSCREEN_WIDTH *2 /3);
  130. CGFloat p1 = size.height/size.width;
  131. CGFloat p2 = 1920./1080.; //使用了1080p的图像输出
  132. if (p1 < p2) {
  133. CGFloat fixHeight = LWSCREEN_WIDTH * 1920. / 1080.;
  134. CGFloat fixPadding = (fixHeight - size.height)/2;
  135. _captureOutput.rectOfInterest = CGRectMake((cropRect.origin.y + fixPadding)/fixHeight,
  136. cropRect.origin.x/size.width,
  137. cropRect.size.height/fixHeight,
  138. cropRect.size.width/size.width);
  139. } else {
  140. CGFloat fixWidth = LWSCREENH_HEIGHT * 1080. / 1920.;
  141. CGFloat fixPadding = (fixWidth - size.width)/2;
  142. _captureOutput.rectOfInterest = CGRectMake(cropRect.origin.y/size.height,
  143. (cropRect.origin.x + fixPadding)/fixWidth,
  144. cropRect.size.height/size.height,
  145. cropRect.size.width/fixWidth);
  146. NSLog(@"rectOfInterest ====%@=====", NSStringFromCGRect(_captureOutput.rectOfInterest));
  147. //
  148. }
  149. // _captureOutput.rectOfInterest = CGRectMake(0.08, 0.08, 0.6, 1);
  150. [_captureOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
  151. [_captureOutput setMetadataObjectTypes:@[AVMetadataObjectTypeAztecCode,AVMetadataObjectTypeCode39Code,
  152. AVMetadataObjectTypeCode93Code,AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeDataMatrixCode,AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeEAN13Code,AVMetadataObjectTypeITF14Code,AVMetadataObjectTypePDF417Code,AVMetadataObjectTypeQRCode,AVMetadataObjectTypeUPCECode]];
  153. [_captureSession addConnection:connection];
  154. [self setCaptureSessionPreset:_resolutionPreset];
  155. return self;
  156. }
  157. - (void)setCaptureSessionPreset:(ResolutionPreset)resolutionPreset {
  158. switch (resolutionPreset) {
  159. case max:
  160. if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
  161. _captureSession.sessionPreset = AVCaptureSessionPresetHigh;
  162. _previewSize =
  163. CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width,
  164. _captureDevice.activeFormat.highResolutionStillImageDimensions.height);
  165. break;
  166. }
  167. case ultraHigh:
  168. if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset3840x2160]) {
  169. _captureSession.sessionPreset = AVCaptureSessionPreset3840x2160;
  170. _previewSize = CGSizeMake(3840, 2160);
  171. break;
  172. }
  173. case veryHigh:
  174. if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {
  175. _captureSession.sessionPreset = AVCaptureSessionPreset1920x1080;
  176. _previewSize = CGSizeMake(1920, 1080);
  177. break;
  178. }
  179. case high:
  180. if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
  181. _captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
  182. _previewSize = CGSizeMake(1280, 720);
  183. break;
  184. }
  185. case medium:
  186. if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) {
  187. _captureSession.sessionPreset = AVCaptureSessionPreset640x480;
  188. _previewSize = CGSizeMake(640, 480);
  189. break;
  190. }
  191. case low:
  192. if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset352x288]) {
  193. _captureSession.sessionPreset = AVCaptureSessionPreset352x288;
  194. _previewSize = CGSizeMake(352, 288);
  195. break;
  196. }
  197. default:
  198. if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) {
  199. _captureSession.sessionPreset = AVCaptureSessionPresetLow;
  200. _previewSize = CGSizeMake(352, 288);
  201. } else {
  202. NSError *error =
  203. [NSError errorWithDomain:NSCocoaErrorDomain
  204. code:NSURLErrorUnknown
  205. userInfo:@{
  206. NSLocalizedDescriptionKey :
  207. @"No capture session available for current capture session."
  208. }];
  209. @throw error;
  210. }
  211. }
  212. }
  213. - (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
  214. if (metadataObjects.count>0) {
  215. AVMetadataMachineReadableCodeObject * metaObject=metadataObjects[0];
  216. NSString * value=metaObject.stringValue;
  217. if(value.length&&_eventSink){
  218. _eventSink([RScanResult toMap:metaObject]);
  219. }
  220. }
  221. }
  222. - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
  223. if (output == _captureVideoOutput) {
  224. CVPixelBufferRef newBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
  225. CFRetain(newBuffer);
  226. CVPixelBufferRef old = _latestPixelBuffer;
  227. while (!OSAtomicCompareAndSwapPtrBarrier(old, newBuffer, (void **)&_latestPixelBuffer)) {
  228. old = _latestPixelBuffer;
  229. }
  230. if (old != nil) {
  231. CFRelease(old);
  232. }
  233. if (_onFrameAvailable) {
  234. _onFrameAvailable();
  235. }
  236. }
  237. }
  238. - (void)start{
  239. [_captureSession startRunning];
  240. }
  241. - (void)stop{
  242. [_captureSession stopRunning];
  243. }
  244. -(void)resume{
  245. if(![_captureSession isRunning]){
  246. [_captureSession startRunning];
  247. }
  248. }
  249. -(void)pause{
  250. if ([_captureSession isRunning]) {
  251. [_captureSession stopRunning];
  252. }
  253. }
  254. -(BOOL)setFlashMode:(BOOL) isOpen{
  255. [_captureDevice lockForConfiguration:nil];
  256. BOOL isSuccess = YES;
  257. if ([_captureDevice hasFlash]) {
  258. if (isOpen) {
  259. _captureDevice.flashMode=AVCaptureFlashModeOn;
  260. _captureDevice.torchMode=AVCaptureTorchModeOn;
  261. }else{
  262. _captureDevice.flashMode = AVCaptureFlashModeOff;
  263. _captureDevice.torchMode = AVCaptureTorchModeOff;
  264. }
  265. }else{
  266. isSuccess=NO;
  267. }
  268. [_captureDevice unlockForConfiguration];
  269. return isSuccess;
  270. }
  271. -(BOOL)getFlashMode{
  272. [_captureDevice lockForConfiguration:nil];
  273. BOOL isSuccess = _captureDevice.flashMode==AVCaptureFlashModeOn&&
  274. _captureDevice.torchMode==AVCaptureTorchModeOn;
  275. [_captureDevice unlockForConfiguration];
  276. return isSuccess;
  277. }
  278. - (void)close {
  279. [_captureSession stopRunning];
  280. for (AVCaptureInput *input in [_captureSession inputs]) {
  281. [_captureSession removeInput:input];
  282. }
  283. for (AVCaptureOutput *output in [_captureSession outputs]) {
  284. [_captureSession removeOutput:output];
  285. }
  286. }
  287. - (CVPixelBufferRef _Nullable)copyPixelBuffer {
  288. CVPixelBufferRef pixelBuffer = _latestPixelBuffer;
  289. while (!OSAtomicCompareAndSwapPtrBarrier(pixelBuffer, nil, (void **)&_latestPixelBuffer)) {
  290. pixelBuffer = _latestPixelBuffer;
  291. }
  292. return pixelBuffer;
  293. }
  294. - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
  295. _eventSink = nil;
  296. [_eventChannel setStreamHandler:nil];
  297. return nil;
  298. }
  299. - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
  300. _eventSink =events;
  301. return nil;
  302. }
  303. @end
  304. @interface RScanCamera()
  305. @property(readonly, nonatomic) RScanFLTCam *camera;
  306. @end
  307. @implementation RScanCamera{
  308. dispatch_queue_t _dispatchQueue;
  309. }
  310. - (instancetype)initWithRegistry:(NSObject<FlutterTextureRegistry> *)registry
  311. messenger:(NSObject<FlutterBinaryMessenger> *)messenger {
  312. self = [super init];
  313. NSAssert(self, @"super init cannot be nil");
  314. _registry = registry;
  315. _messenger = messenger;
  316. return self;
  317. }
  318. + (void)registerWithRegistrar:(nonnull NSObject<FlutterPluginRegistrar> *)registrar {
  319. }
  320. - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result{
  321. if ([@"availableCameras" isEqualToString:call.method]) {
  322. [self availableCameras:call result:result];
  323. }else if([@"initialize" isEqualToString:call.method]){
  324. [self initialize:call result:result];
  325. }else if([@"dispose" isEqualToString:call.method]){
  326. [self dispose:call result:result];
  327. }else if ([call.method isEqualToString:@"startScan"]) {
  328. [_camera resume];
  329. result(nil);
  330. }else if([call.method isEqualToString:@"stopScan"]){
  331. [_camera pause];
  332. result(nil);
  333. }else if ([call.method isEqualToString:@"setFlashMode"]){
  334. NSNumber * isOpen = [call.arguments valueForKey:@"isOpen"];
  335. result([NSNumber numberWithBool:[_camera setFlashMode:[isOpen boolValue]]]);
  336. }else if ([call.method isEqualToString:@"getFlashMode"]){
  337. result([NSNumber numberWithBool:[_camera getFlashMode]]);
  338. }else{
  339. result(FlutterMethodNotImplemented);
  340. }
  341. }
  342. /**
  343. 获取可用的摄像头
  344. */
  345. -(void)availableCameras:(FlutterMethodCall *)call result:(FlutterResult)result{
  346. AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession
  347. discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInWideAngleCamera ]
  348. mediaType:AVMediaTypeVideo
  349. position:AVCaptureDevicePositionUnspecified];
  350. NSArray<AVCaptureDevice *> *devices = discoverySession.devices;
  351. NSMutableArray<NSDictionary<NSString *, NSObject *> *> *reply =
  352. [[NSMutableArray alloc] initWithCapacity:devices.count];
  353. for (AVCaptureDevice *device in devices) {
  354. NSString *lensFacing;
  355. switch ([device position]) {
  356. case AVCaptureDevicePositionBack:
  357. lensFacing = @"back";
  358. break;
  359. case AVCaptureDevicePositionFront:
  360. lensFacing = @"front";
  361. break;
  362. case AVCaptureDevicePositionUnspecified:
  363. lensFacing = @"external";
  364. break;
  365. }
  366. [reply addObject:@{
  367. @"name" : [device uniqueID],
  368. @"lensFacing" : lensFacing
  369. }];
  370. }
  371. result(reply);
  372. }
  373. /**
  374. 初始化相机
  375. */
  376. -(void)initialize:(FlutterMethodCall *)call result:(FlutterResult)result {
  377. NSString *cameraName = call.arguments[@"cameraName"];
  378. NSString *resolutionPreset = call.arguments[@"resolutionPreset"];
  379. NSError * error;
  380. RScanFLTCam* cam =[[RScanFLTCam alloc]initWitchCameraName:cameraName resolutionPreset:resolutionPreset dispatchQueue:_dispatchQueue error:&error];
  381. if(error){
  382. result(getFlutterError(error));
  383. return;
  384. }else{
  385. if(_camera){
  386. [_camera close];
  387. }
  388. int64_t textureId = [_registry registerTexture:cam];
  389. _camera =cam;
  390. cam.onFrameAvailable = ^{
  391. [self->_registry textureFrameAvailable:textureId];
  392. };
  393. FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:[NSString stringWithFormat:@"com.rhyme_lph/r_scan_camera_%lld/event",textureId] binaryMessenger:_messenger];
  394. [eventChannel setStreamHandler:cam];
  395. cam.eventChannel = eventChannel;
  396. result(@{
  397. @"textureId":@(textureId),
  398. @"previewWidth":@([UIScreen mainScreen].bounds.size.width),
  399. @"previewHeight":@([UIScreen mainScreen].bounds.size.height)
  400. // @"previewWidth":@(cam.previewSize.width),
  401. // @"previewHeight":@(cam.previewSize.height)
  402. });
  403. [cam start];
  404. }
  405. }
  406. /**
  407. 销毁相机
  408. */
  409. -(void)dispose:(FlutterMethodCall *)call result:(FlutterResult)result {
  410. NSDictionary *argsMap = call.arguments;
  411. NSUInteger textureId = ((NSNumber *)argsMap[@"textureId"]).unsignedIntegerValue;
  412. [_registry unregisterTexture:textureId];
  413. [_camera close];
  414. _dispatchQueue = nil;
  415. result(nil);
  416. }
  417. @end