SDL_camera_apple.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 2021 Valve Corporation
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #ifdef SDL_VIDEO_CAPTURE
  20. #include "SDL3/SDL.h"
  21. #include "SDL3/SDL_video_capture.h"
  22. #include "SDL_sysvideocapture.h"
  23. #include "SDL_video_capture_c.h"
  24. #include "../thread/SDL_systhread.h"
  25. #if defined(HAVE_COREMEDIA) && defined(SDL_PLATFORM_MACOS) && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101500)
  26. /* AVCaptureDeviceTypeBuiltInWideAngleCamera requires macOS SDK 10.15 */
  27. #undef HAVE_COREMEDIA
  28. #endif
  29. #ifdef SDL_PLATFORM_TVOS
  30. #undef HAVE_COREMEDIA
  31. #endif
  32. #ifndef HAVE_COREMEDIA
  33. int InitDevice(SDL_VideoCaptureDevice *_this) {
  34. return -1;
  35. }
  36. int OpenDevice(SDL_VideoCaptureDevice *_this) {
  37. return -1;
  38. }
  39. int AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) {
  40. return -1;
  41. }
  42. void CloseDevice(SDL_VideoCaptureDevice *_this) {
  43. }
  44. int GetDeviceName(SDL_VideoCaptureDeviceID instance_id, char *buf, int size) {
  45. return -1;
  46. }
  47. int GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec) {
  48. return -1;
  49. }
  50. int GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format) {
  51. return -1;
  52. }
  53. int GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width, int *height) {
  54. return -1;
  55. }
  56. SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count) {
  57. return NULL;
  58. }
  59. int GetNumFormats(SDL_VideoCaptureDevice *_this) {
  60. return 0;
  61. }
  62. int GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format) {
  63. return 0;
  64. }
  65. int ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) {
  66. return 0;
  67. }
  68. int StartCapture(SDL_VideoCaptureDevice *_this) {
  69. return 0;
  70. }
  71. int StopCapture(SDL_VideoCaptureDevice *_this) {
  72. return 0;
  73. }
  74. int SDL_SYS_VideoCaptureInit(void) {
  75. return 0;
  76. }
  77. int SDL_SYS_VideoCaptureQuit(void) {
  78. return 0;
  79. }
  80. #else
  81. #import <AVFoundation/AVFoundation.h>
  82. #import <CoreMedia/CoreMedia.h>
  83. /*
  84. * Need to link with:: CoreMedia CoreVideo
  85. *
  86. * Add in pInfo.list:
  87. * <key>NSCameraUsageDescription</key> <string>Access camera</string>
  88. *
  89. *
  90. * MACOSX:
  91. * Add to the Code Sign Entitlement file:
  92. * <key>com.apple.security.device.camera</key> <true/>
  93. *
  94. *
  95. * IOS:
  96. *
  97. * - Need to link with:: CoreMedia CoreVideo
  98. * - Add #define SDL_VIDEO_CAPTURE 1
  99. * to SDL_build_config_ios.h
  100. */
  101. @class MySampleBufferDelegate;
  102. struct SDL_PrivateVideoCaptureData
  103. {
  104. dispatch_queue_t queue;
  105. MySampleBufferDelegate *delegate;
  106. AVCaptureSession *session;
  107. CMSimpleQueueRef frame_queue;
  108. };
  109. static NSString *
  110. fourcc_to_nstring(Uint32 code)
  111. {
  112. Uint8 buf[4];
  113. *(Uint32 *)buf = code;
  114. return [NSString stringWithFormat:@"%c%c%c%c", buf[3], buf[2], buf[1], buf[0]];
  115. }
  116. static NSArray<AVCaptureDevice *> *
  117. discover_devices()
  118. {
  119. NSArray *deviceType = @[AVCaptureDeviceTypeBuiltInWideAngleCamera];
  120. AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession
  121. discoverySessionWithDeviceTypes:deviceType
  122. mediaType:AVMediaTypeVideo
  123. position:AVCaptureDevicePositionUnspecified];
  124. NSArray<AVCaptureDevice *> *devices = discoverySession.devices;
  125. if ([devices count] > 0) {
  126. return devices;
  127. } else {
  128. AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  129. if (captureDevice == nil) {
  130. return devices;
  131. } else {
  132. NSArray<AVCaptureDevice *> *default_device = @[ captureDevice ];
  133. return default_device;
  134. }
  135. }
  136. return devices;
  137. }
  138. static AVCaptureDevice *
  139. get_device_by_name(const char *dev_name)
  140. {
  141. NSArray<AVCaptureDevice *> *devices = discover_devices();
  142. for (AVCaptureDevice *device in devices) {
  143. char buf[1024];
  144. NSString *cameraID = [device localizedName];
  145. const char *str = [cameraID UTF8String];
  146. SDL_snprintf(buf, sizeof (buf) - 1, "%s", str);
  147. if (SDL_strcmp(buf, dev_name) == 0) {
  148. return device;
  149. }
  150. }
  151. return nil;
  152. }
  153. static Uint32
  154. nsfourcc_to_sdlformat(NSString *nsfourcc)
  155. {
  156. const char *str = [nsfourcc UTF8String];
  157. /* FIXME
  158. * on IOS this mode gives 2 planes, and it's NV12
  159. * on macos, 1 plane/ YVYU
  160. *
  161. */
  162. #ifdef SDL_PLATFORM_MACOS
  163. if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_YVYU;
  164. #else
  165. if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_NV12;
  166. #endif
  167. if (SDL_strcmp("yuvs", str) == 0) return SDL_PIXELFORMAT_UYVY;
  168. if (SDL_strcmp("420f", str) == 0) return SDL_PIXELFORMAT_UNKNOWN;
  169. SDL_Log("Unknown format '%s'", str);
  170. return SDL_PIXELFORMAT_UNKNOWN;
  171. }
  172. static NSString *
  173. sdlformat_to_nsfourcc(Uint32 fmt)
  174. {
  175. const char *str = "";
  176. NSString *result;
  177. #ifdef SDL_PLATFORM_MACOS
  178. if (fmt == SDL_PIXELFORMAT_YVYU) str = "420v";
  179. #else
  180. if (fmt == SDL_PIXELFORMAT_NV12) str = "420v";
  181. #endif
  182. if (fmt == SDL_PIXELFORMAT_UYVY) str = "yuvs";
  183. result = [[NSString alloc] initWithUTF8String: str];
  184. return result;
  185. }
  186. @interface MySampleBufferDelegate : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate>
  187. @property struct SDL_PrivateVideoCaptureData *hidden;
  188. - (void) set: (struct SDL_PrivateVideoCaptureData *) val;
  189. @end
  190. @implementation MySampleBufferDelegate
  191. - (void) set: (struct SDL_PrivateVideoCaptureData *) val {
  192. _hidden = val;
  193. }
  194. - (void) captureOutput:(AVCaptureOutput *)output
  195. didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
  196. fromConnection:(AVCaptureConnection *) connection {
  197. CFRetain(sampleBuffer);
  198. CMSimpleQueueEnqueue(_hidden->frame_queue, sampleBuffer);
  199. }
  200. - (void)captureOutput:(AVCaptureOutput *)output
  201. didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer
  202. fromConnection:(AVCaptureConnection *)connection {
  203. SDL_Log("Drop frame..");
  204. }
  205. @end
  206. int
  207. OpenDevice(SDL_VideoCaptureDevice *_this)
  208. {
  209. _this->hidden = (struct SDL_PrivateVideoCaptureData *) SDL_calloc(1, sizeof (struct SDL_PrivateVideoCaptureData));
  210. if (_this->hidden == NULL) {
  211. SDL_OutOfMemory();
  212. goto error;
  213. }
  214. return 0;
  215. error:
  216. return -1;
  217. }
  218. void
  219. CloseDevice(SDL_VideoCaptureDevice *_this)
  220. {
  221. if (!_this) {
  222. return;
  223. }
  224. if (_this->hidden) {
  225. AVCaptureSession *session = _this->hidden->session;
  226. if (session) {
  227. AVCaptureInput *input;
  228. AVCaptureVideoDataOutput *output;
  229. input = [session.inputs objectAtIndex:0];
  230. [session removeInput:input];
  231. output = (AVCaptureVideoDataOutput*)[session.outputs objectAtIndex:0];
  232. [session removeOutput:output];
  233. // TODO more cleanup ?
  234. }
  235. if (_this->hidden->frame_queue) {
  236. CFRelease(_this->hidden->frame_queue);
  237. }
  238. SDL_free(_this->hidden);
  239. _this->hidden = NULL;
  240. }
  241. }
  242. int
  243. InitDevice(SDL_VideoCaptureDevice *_this)
  244. {
  245. NSString *fmt = sdlformat_to_nsfourcc(_this->spec.format);
  246. int w = _this->spec.width;
  247. int h = _this->spec.height;
  248. NSError *error = nil;
  249. AVCaptureDevice *device = nil;
  250. AVCaptureDeviceInput *input = nil;
  251. AVCaptureVideoDataOutput *output = nil;
  252. AVCaptureDeviceFormat *spec_format = nil;
  253. #ifdef SDL_PLATFORM_MACOS
  254. if (@available(macOS 10.15, *)) {
  255. /* good. */
  256. } else {
  257. return -1;
  258. }
  259. #endif
  260. device = get_device_by_name(_this->dev_name);
  261. if (!device) {
  262. goto error;
  263. }
  264. _this->hidden->session = [[AVCaptureSession alloc] init];
  265. if (_this->hidden->session == nil) {
  266. goto error;
  267. }
  268. [_this->hidden->session setSessionPreset:AVCaptureSessionPresetHigh];
  269. // Pick format that matches the spec
  270. {
  271. NSArray<AVCaptureDeviceFormat *> *formats = [device formats];
  272. for (AVCaptureDeviceFormat *format in formats) {
  273. CMFormatDescriptionRef formatDescription = [format formatDescription];
  274. FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription);
  275. NSString *str = fourcc_to_nstring(mediaSubType);
  276. if (str == fmt) {
  277. CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription);
  278. if (dim.width == w && dim.height == h) {
  279. spec_format = format;
  280. break;
  281. }
  282. }
  283. }
  284. }
  285. if (spec_format == nil) {
  286. SDL_SetError("format not found");
  287. goto error;
  288. }
  289. // Set format
  290. if ([device lockForConfiguration:NULL] == YES) {
  291. device.activeFormat = spec_format;
  292. [device unlockForConfiguration];
  293. } else {
  294. SDL_SetError("Cannot lockForConfiguration");
  295. goto error;
  296. }
  297. // Input
  298. input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
  299. if (!input) {
  300. SDL_SetError("Cannot create AVCaptureDeviceInput");
  301. goto error;
  302. }
  303. // Output
  304. output = [[AVCaptureVideoDataOutput alloc] init];
  305. #ifdef SDL_PLATFORM_MACOS
  306. // FIXME this now fail on ios ... but not using anything works...
  307. // Specify the pixel format
  308. output.videoSettings =
  309. [NSDictionary dictionaryWithObject:
  310. [NSNumber numberWithInt:kCVPixelFormatType_422YpCbCr8]
  311. forKey:(id)kCVPixelBufferPixelFormatTypeKey];
  312. #endif
  313. _this->hidden->delegate = [[MySampleBufferDelegate alloc] init];
  314. [_this->hidden->delegate set:_this->hidden];
  315. CMSimpleQueueCreate(kCFAllocatorDefault, 30 /* buffers */, &_this->hidden->frame_queue);
  316. if (_this->hidden->frame_queue == nil) {
  317. goto error;
  318. }
  319. _this->hidden->queue = dispatch_queue_create("my_queue", NULL);
  320. [output setSampleBufferDelegate:_this->hidden->delegate queue:_this->hidden->queue];
  321. if ([_this->hidden->session canAddInput:input] ){
  322. [_this->hidden->session addInput:input];
  323. } else {
  324. SDL_SetError("Cannot add AVCaptureDeviceInput");
  325. goto error;
  326. }
  327. if ([_this->hidden->session canAddOutput:output] ){
  328. [_this->hidden->session addOutput:output];
  329. } else {
  330. SDL_SetError("Cannot add AVCaptureVideoDataOutput");
  331. goto error;
  332. }
  333. [_this->hidden->session commitConfiguration];
  334. return 0;
  335. error:
  336. return -1;
  337. }
  338. int
  339. GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec)
  340. {
  341. if (spec) {
  342. *spec = _this->spec;
  343. return 0;
  344. }
  345. return -1;
  346. }
  347. int
  348. StartCapture(SDL_VideoCaptureDevice *_this)
  349. {
  350. [_this->hidden->session startRunning];
  351. return 0;
  352. }
  353. int
  354. StopCapture(SDL_VideoCaptureDevice *_this)
  355. {
  356. [_this->hidden->session stopRunning];
  357. return 0;
  358. }
  359. int
  360. AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame)
  361. {
  362. if (CMSimpleQueueGetCount(_this->hidden->frame_queue) > 0) {
  363. int i, numPlanes, planar;
  364. CMSampleBufferRef sampleBuffer;
  365. CVImageBufferRef image;
  366. sampleBuffer = (CMSampleBufferRef)CMSimpleQueueDequeue(_this->hidden->frame_queue);
  367. frame->internal = (void *) sampleBuffer;
  368. frame->timestampNS = SDL_GetTicksNS();
  369. i = 0;
  370. image = CMSampleBufferGetImageBuffer(sampleBuffer);
  371. numPlanes = CVPixelBufferGetPlaneCount(image);
  372. planar = CVPixelBufferIsPlanar(image);
  373. #if 0
  374. int w = CVPixelBufferGetWidth(image);
  375. int h = CVPixelBufferGetHeight(image);
  376. int sz = CVPixelBufferGetDataSize(image);
  377. int pitch = CVPixelBufferGetBytesPerRow(image);
  378. SDL_Log("buffer planar=%d count:%d %d x %d sz=%d pitch=%d", planar, numPlanes, w, h, sz, pitch);
  379. #endif
  380. CVPixelBufferLockBaseAddress(image, 0);
  381. if (planar == 0 && numPlanes == 0) {
  382. frame->pitch[0] = CVPixelBufferGetBytesPerRow(image);
  383. frame->data[0] = CVPixelBufferGetBaseAddress(image);
  384. frame->num_planes = 1;
  385. } else {
  386. for (i = 0; i < numPlanes && i < 3; i++) {
  387. int rowStride = 0;
  388. uint8_t *data = NULL;
  389. frame->num_planes += 1;
  390. rowStride = CVPixelBufferGetBytesPerRowOfPlane(image, i);
  391. data = CVPixelBufferGetBaseAddressOfPlane(image, i);
  392. frame->data[i] = data;
  393. frame->pitch[i] = rowStride;
  394. }
  395. }
  396. /* Unlocked when frame is released */
  397. } else {
  398. // no frame
  399. SDL_Delay(20); // TODO fix some delay
  400. }
  401. return 0;
  402. }
  403. int
  404. ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame)
  405. {
  406. if (frame->internal){
  407. CMSampleBufferRef sampleBuffer = (CMSampleBufferRef) frame->internal;
  408. CVImageBufferRef image = CMSampleBufferGetImageBuffer(sampleBuffer);
  409. CVPixelBufferUnlockBaseAddress(image, 0);
  410. CFRelease(sampleBuffer);
  411. }
  412. return 0;
  413. }
  414. int
  415. GetNumFormats(SDL_VideoCaptureDevice *_this)
  416. {
  417. AVCaptureDevice *device = get_device_by_name(_this->dev_name);
  418. if (device) {
  419. // LIST FORMATS
  420. NSMutableOrderedSet<NSString *> *array_formats = [NSMutableOrderedSet new];
  421. NSArray<AVCaptureDeviceFormat *> *formats = [device formats];
  422. for (AVCaptureDeviceFormat *format in formats) {
  423. // NSLog(@"%@", formats);
  424. CMFormatDescriptionRef formatDescription = [format formatDescription];
  425. //NSLog(@"%@", formatDescription);
  426. FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription);
  427. NSString *str = fourcc_to_nstring(mediaSubType);
  428. [array_formats addObject:str];
  429. }
  430. return [array_formats count];
  431. }
  432. return 0;
  433. }
  434. int
  435. GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format)
  436. {
  437. AVCaptureDevice *device = get_device_by_name(_this->dev_name);
  438. if (device) {
  439. // LIST FORMATS
  440. NSMutableOrderedSet<NSString *> *array_formats = [NSMutableOrderedSet new];
  441. NSArray<AVCaptureDeviceFormat *> *formats = [device formats];
  442. NSString *str;
  443. for (AVCaptureDeviceFormat *f in formats) {
  444. FourCharCode mediaSubType;
  445. CMFormatDescriptionRef formatDescription;
  446. formatDescription = [f formatDescription];
  447. mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription);
  448. str = fourcc_to_nstring(mediaSubType);
  449. [array_formats addObject:str];
  450. }
  451. str = array_formats[index];
  452. *format = nsfourcc_to_sdlformat(str);
  453. return 0;
  454. }
  455. return -1;
  456. }
  457. int
  458. GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format)
  459. {
  460. AVCaptureDevice *device = get_device_by_name(_this->dev_name);
  461. if (device) {
  462. NSString *fmt = sdlformat_to_nsfourcc(format);
  463. int count = 0;
  464. NSArray<AVCaptureDeviceFormat *> *formats = [device formats];
  465. for (AVCaptureDeviceFormat *f in formats) {
  466. CMFormatDescriptionRef formatDescription = [f formatDescription];
  467. FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription);
  468. NSString *str = fourcc_to_nstring(mediaSubType);
  469. if (str == fmt) {
  470. count += 1;
  471. }
  472. }
  473. return count;
  474. }
  475. return 0;
  476. }
  477. int
  478. GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width, int *height)
  479. {
  480. AVCaptureDevice *device = get_device_by_name(_this->dev_name);
  481. if (device) {
  482. NSString *fmt = sdlformat_to_nsfourcc(format);
  483. int count = 0;
  484. NSArray<AVCaptureDeviceFormat *> *formats = [device formats];
  485. for (AVCaptureDeviceFormat *f in formats) {
  486. CMFormatDescriptionRef formatDescription = [f formatDescription];
  487. FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription);
  488. NSString *str = fourcc_to_nstring(mediaSubType);
  489. if (str == fmt) {
  490. if (index == count) {
  491. CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription);
  492. *width = dim.width;
  493. *height = dim.height;
  494. return 0;
  495. }
  496. count += 1;
  497. }
  498. }
  499. }
  500. return -1;
  501. }
  502. int
  503. GetDeviceName(SDL_VideoCaptureDeviceID instance_id, char *buf, int size)
  504. {
  505. int index = instance_id - 1;
  506. NSArray<AVCaptureDevice *> *devices = discover_devices();
  507. if (index < [devices count]) {
  508. AVCaptureDevice *device = devices[index];
  509. NSString *cameraID = [device localizedName];
  510. const char *str = [cameraID UTF8String];
  511. SDL_snprintf(buf, size, "%s", str);
  512. return 0;
  513. }
  514. return -1;
  515. }
  516. static int
  517. GetNumDevices(void)
  518. {
  519. NSArray<AVCaptureDevice *> *devices = discover_devices();
  520. return [devices count];
  521. }
  522. SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count)
  523. {
  524. /* hard-coded list of ID */
  525. int i;
  526. int num = GetNumDevices();
  527. SDL_VideoCaptureDeviceID *ret;
  528. ret = (SDL_VideoCaptureDeviceID *)SDL_malloc((num + 1) * sizeof(*ret));
  529. if (ret == NULL) {
  530. SDL_OutOfMemory();
  531. *count = 0;
  532. return NULL;
  533. }
  534. for (i = 0; i < num; i++) {
  535. ret[i] = i + 1;
  536. }
  537. ret[num] = 0;
  538. *count = num;
  539. return ret;
  540. }
  541. int SDL_SYS_VideoCaptureInit(void)
  542. {
  543. return 0;
  544. }
  545. int SDL_SYS_VideoCaptureQuit(void)
  546. {
  547. return 0;
  548. }
  549. #endif /* HAVE_COREMEDIA */
  550. #endif /* SDL_VIDEO_CAPTURE */