CameraCapture.mm 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #if !PLATFORM_TVOS && UNITY_USES_WEBCAM
  2. #include "CameraCapture.h"
  3. #include "AVCapture.h"
  4. #include "CMVideoSampling.h"
  5. #include "CVTextureCache.h"
  6. #import <CoreVideo/CoreVideo.h>
  7. #include <cmath>
  8. @implementation CameraCaptureController
  9. {
  10. AVCaptureDevice* _captureDevice;
  11. AVCaptureSession* _captureSession;
  12. AVCaptureDeviceInput* _captureInput;
  13. AVCaptureVideoDataOutput* _captureOutput;
  14. @public CMVideoSampling _cmVideoSampling;
  15. @public void* _userData;
  16. @public size_t _width, _height;
  17. }
  18. - (bool)initCapture:(AVCaptureDevice*)device width:(int)w height:(int)h fps:(float)fps
  19. {
  20. if (UnityGetAVCapturePermission(avVideoCapture) == avCapturePermissionDenied)
  21. return false;
  22. self.captureDevice = device;
  23. self.captureInput = [AVCaptureDeviceInput deviceInputWithDevice: device error: nil];
  24. self.captureOutput = [[AVCaptureVideoDataOutput alloc] init];
  25. if (self.captureOutput == nil || self.captureInput == nil)
  26. return false;
  27. self.captureOutput.alwaysDiscardsLateVideoFrames = YES;
  28. if ([device lockForConfiguration: nil])
  29. {
  30. AVFrameRateRange* range = [self pickFrameRateRange: fps];
  31. if (range)
  32. {
  33. device.activeVideoMinFrameDuration = range.minFrameDuration;
  34. device.activeVideoMaxFrameDuration = range.maxFrameDuration;
  35. }
  36. else
  37. {
  38. #pragma clang diagnostic push
  39. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  40. self.captureOutput.minFrameDuration = CMTimeMake(1, fps);
  41. #pragma clang diagnostic pop
  42. }
  43. [device unlockForConfiguration];
  44. }
  45. // queue on main thread to simplify gles life
  46. [self.captureOutput setSampleBufferDelegate: self queue: dispatch_get_main_queue()];
  47. NSDictionary* options = @{ (NSString*)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA) };
  48. [self.captureOutput setVideoSettings: options];
  49. self.captureSession = [[AVCaptureSession alloc] init];
  50. [self.captureSession addInput: self.captureInput];
  51. [self.captureSession addOutput: self.captureOutput];
  52. self.captureSession.sessionPreset = [self pickPresetFromWidth: w height: h];
  53. CMVideoSampling_Initialize(&self->_cmVideoSampling);
  54. _width = _height = 0;
  55. return true;
  56. }
  57. - (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection
  58. {
  59. intptr_t tex = (intptr_t)CMVideoSampling_SampleBuffer(&self->_cmVideoSampling, sampleBuffer, &_width, &_height);
  60. UnityDidCaptureVideoFrame(tex, self->_userData);
  61. }
  62. - (void)start { [self.captureSession startRunning]; }
  63. - (void)pause { [self.captureSession stopRunning]; }
  64. - (void)stop
  65. {
  66. [self.captureSession stopRunning];
  67. [self.captureSession removeInput: self.captureInput];
  68. [self.captureSession removeOutput: self.captureOutput];
  69. self.captureDevice = nil;
  70. self.captureInput = nil;
  71. self.captureOutput = nil;
  72. self.captureSession = nil;
  73. CMVideoSampling_Uninitialize(&self->_cmVideoSampling);
  74. }
  75. - (NSString*)pickPresetFromWidth:(int)w height:(int)h
  76. {
  77. static NSString* preset[] =
  78. {
  79. AVCaptureSessionPreset352x288,
  80. AVCaptureSessionPreset640x480,
  81. AVCaptureSessionPreset1280x720,
  82. AVCaptureSessionPreset1920x1080,
  83. };
  84. static int presetW[] = { 352, 640, 1280, 1920 };
  85. #define countof(arr) sizeof(arr)/sizeof(arr[0])
  86. static_assert(countof(presetW) == countof(preset), "preset and preset width arrrays have different elem count");
  87. int ret = -1, curW = -10000;
  88. for (int i = 0, n = countof(presetW); i < n; ++i)
  89. {
  90. if (::abs(w - presetW[i]) < ::abs(w - curW) && [self.captureSession canSetSessionPreset: preset[i]])
  91. {
  92. ret = i;
  93. curW = presetW[i];
  94. }
  95. }
  96. NSAssert(ret != -1, @"Cannot pick capture preset");
  97. return ret != -1 ? preset[ret] : AVCaptureSessionPresetHigh;
  98. #undef countof
  99. }
  100. - (AVFrameRateRange*)pickFrameRateRange:(float)fps
  101. {
  102. AVFrameRateRange* ret = nil;
  103. float minDiff = INFINITY;
  104. // In some corner cases (seeing this on iPod iOS 6.1.5) activeFormat is null.
  105. if (!self.captureDevice.activeFormat)
  106. return nil;
  107. for (AVFrameRateRange* rate in self.captureDevice.activeFormat.videoSupportedFrameRateRanges)
  108. {
  109. float bestMatch = rate.minFrameRate;
  110. if (fps > rate.maxFrameRate)
  111. bestMatch = rate.maxFrameRate;
  112. else if (fps > rate.minFrameRate)
  113. bestMatch = fps;
  114. float diff = ::fabs(fps - bestMatch);
  115. if (diff < minDiff)
  116. {
  117. minDiff = diff;
  118. ret = rate;
  119. }
  120. }
  121. NSAssert(ret != nil, @"Cannot pick frame rate range");
  122. if (ret == nil)
  123. ret = self.captureDevice.activeFormat.videoSupportedFrameRateRanges[0];
  124. return ret;
  125. }
  126. @synthesize captureDevice = _captureDevice;
  127. @synthesize captureSession = _captureSession;
  128. @synthesize captureOutput = _captureOutput;
  129. @synthesize captureInput = _captureInput;
  130. @end
  131. extern "C" void UnityEnumVideoCaptureDevices(void* udata, void(*callback)(void* udata, const char* name, int frontFacing))
  132. {
  133. for (AVCaptureDevice* device in[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo])
  134. {
  135. int frontFacing = device.position == AVCaptureDevicePositionFront ? 1 : 0;
  136. callback(udata, [device.localizedName UTF8String], frontFacing);
  137. }
  138. }
  139. extern "C" void* UnityInitCameraCapture(int deviceIndex, int w, int h, int fps, void* udata)
  140. {
  141. AVCaptureDevice* device = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo][deviceIndex];
  142. CameraCaptureController* controller = [CameraCaptureController alloc];
  143. if ([controller initCapture: device width: w height: h fps: (float)fps])
  144. {
  145. controller->_userData = udata;
  146. return (__bridge_retained void*)controller;
  147. }
  148. controller = nil;
  149. return 0;
  150. }
  151. extern "C" void UnityStartCameraCapture(void* capture)
  152. {
  153. [(__bridge CameraCaptureController*)capture start];
  154. }
  155. extern "C" void UnityPauseCameraCapture(void* capture)
  156. {
  157. [(__bridge CameraCaptureController*)capture pause];
  158. }
  159. extern "C" void UnityStopCameraCapture(void* capture)
  160. {
  161. CameraCaptureController* controller = (__bridge_transfer CameraCaptureController*)capture;
  162. [controller stop];
  163. controller = nil;
  164. }
  165. extern "C" void UnityCameraCaptureExtents(void* capture, int* w, int* h)
  166. {
  167. CameraCaptureController* controller = (__bridge CameraCaptureController*)capture;
  168. *w = (int)controller->_width;
  169. *h = (int)controller->_height;
  170. }
  171. extern "C" void UnityCameraCaptureReadToMemory(void* capture, void* dst_, int w, int h)
  172. {
  173. CameraCaptureController* controller = (__bridge CameraCaptureController*)capture;
  174. assert(w == controller->_width && h == controller->_height);
  175. CVPixelBufferRef pbuf = (CVPixelBufferRef)controller->_cmVideoSampling.cvImageBuffer;
  176. const size_t srcRowSize = CVPixelBufferGetBytesPerRow(pbuf);
  177. const size_t dstRowSize = w * sizeof(uint32_t);
  178. const size_t bufSize = srcRowSize * h;
  179. // while not the best way memory-wise, we want to minimize stalling
  180. uint8_t* tmpMem = (uint8_t*)::malloc(bufSize);
  181. CVPixelBufferLockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
  182. {
  183. ::memcpy(tmpMem, CVPixelBufferGetBaseAddress(pbuf), bufSize);
  184. }
  185. CVPixelBufferUnlockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
  186. uint8_t* dst = (uint8_t*)dst_;
  187. uint8_t* src = tmpMem + (h - 1) * srcRowSize;
  188. for (int i = 0, n = h; i < n; ++i)
  189. {
  190. ::memcpy(dst, src, dstRowSize);
  191. dst += dstRowSize;
  192. src -= srcRowSize;
  193. }
  194. ::free(tmpMem);
  195. }
  196. extern "C" int UnityCameraCaptureVideoRotationDeg(void* capture)
  197. {
  198. CameraCaptureController* controller = (__bridge CameraCaptureController*)capture;
  199. // all cams are landscape.
  200. switch (UnityCurrentOrientation())
  201. {
  202. case portrait: return 90;
  203. case portraitUpsideDown: return 270;
  204. case landscapeLeft: return controller.captureDevice.position == AVCaptureDevicePositionFront ? 180 : 0;
  205. case landscapeRight: return controller.captureDevice.position == AVCaptureDevicePositionFront ? 0 : 180;
  206. default: assert(false && "bad orientation returned from UnityCurrentOrientation()"); break;
  207. }
  208. return 0;
  209. }
  210. extern "C" int UnityCameraCaptureVerticallyMirrored(void* capture)
  211. {
  212. CameraCaptureController* controller = (__bridge CameraCaptureController*)capture;
  213. return IsCVTextureFlipped(controller->_cmVideoSampling.cvTextureCacheTexture);
  214. }
  215. #else
  216. // STUBBED OUT UNTIL DEVELOPER FINDs AN AWESOME CAMERA SOLUTION FOR APPLE TV //
  217. extern "C" void UnityEnumVideoCaptureDevices(void* udata, void(*callback)(void* udata, const char* name, int frontFacing))
  218. {
  219. }
  220. extern "C" void* UnityInitCameraCapture(int deviceIndex, int w, int h, int fps, void* udata)
  221. {
  222. return 0;
  223. }
  224. extern "C" void UnityStartCameraCapture(void* capture)
  225. {
  226. }
  227. extern "C" void UnityPauseCameraCapture(void* capture)
  228. {
  229. }
  230. extern "C" void UnityStopCameraCapture(void* capture)
  231. {
  232. }
  233. extern "C" void UnityCameraCaptureExtents(void* capture, int* w, int* h)
  234. {
  235. }
  236. extern "C" void UnityCameraCaptureReadToMemory(void* capture, void* dst_, int w, int h)
  237. {
  238. }
  239. extern "C" int UnityCameraCaptureVideoRotationDeg(void* capture)
  240. {
  241. return 0;
  242. }
  243. extern "C" int UnityCameraCaptureVerticallyMirrored(void* capture)
  244. {
  245. return 0;
  246. }
  247. #endif