The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

404 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. // (This file gets included by juce_mac_NativeCode.mm, rather than being
  19. // compiled on its own).
  20. #if JUCE_INCLUDED_FILE && JUCE_QUICKTIME && JUCE_USE_CAMERA
  21. //==============================================================================
  22. #define QTCaptureCallbackDelegate MakeObjCClassName(QTCaptureCallbackDelegate)
  23. class QTCameraDeviceInteral;
  24. END_JUCE_NAMESPACE
  25. @interface QTCaptureCallbackDelegate : NSObject
  26. {
  27. @public
  28. CameraDevice* owner;
  29. QTCameraDeviceInteral* internal;
  30. Time* firstRecordedTime;
  31. }
  32. - (QTCaptureCallbackDelegate*) initWithOwner: (CameraDevice*) owner internalDev: (QTCameraDeviceInteral*) d;
  33. - (void) dealloc;
  34. - (void) captureOutput: (QTCaptureOutput*) captureOutput
  35. didOutputVideoFrame: (CVImageBufferRef) videoFrame
  36. withSampleBuffer: (QTSampleBuffer*) sampleBuffer
  37. fromConnection: (QTCaptureConnection*) connection;
  38. - (void) captureOutput: (QTCaptureFileOutput*) captureOutput
  39. didOutputSampleBuffer: (QTSampleBuffer*) sampleBuffer
  40. fromConnection: (QTCaptureConnection*) connection;
  41. @end
  42. BEGIN_JUCE_NAMESPACE
  43. //==============================================================================
  44. class QTCameraDeviceInteral
  45. {
  46. public:
  47. QTCameraDeviceInteral (CameraDevice* owner, int index)
  48. {
  49. const ScopedAutoReleasePool pool;
  50. session = [[QTCaptureSession alloc] init];
  51. NSArray* devs = [QTCaptureDevice inputDevicesWithMediaType: QTMediaTypeVideo];
  52. device = (QTCaptureDevice*) [devs objectAtIndex: index];
  53. input = 0;
  54. fileOutput = 0;
  55. imageOutput = 0;
  56. callbackDelegate = [[QTCaptureCallbackDelegate alloc] initWithOwner: owner
  57. internalDev: this];
  58. NSError* err = 0;
  59. [device retain];
  60. [device open: &err];
  61. if (err == 0)
  62. {
  63. input = [[QTCaptureDeviceInput alloc] initWithDevice: device];
  64. [session addInput: input error: &err];
  65. if (err == 0)
  66. {
  67. resetFile();
  68. imageOutput = [[QTCaptureDecompressedVideoOutput alloc] init];
  69. [imageOutput setDelegate: callbackDelegate];
  70. if (err == 0)
  71. {
  72. [session startRunning];
  73. return;
  74. }
  75. }
  76. }
  77. openingError = nsStringToJuce ([err description]);
  78. DBG (openingError);
  79. }
  80. ~QTCameraDeviceInteral()
  81. {
  82. [session stopRunning];
  83. [session removeOutput: imageOutput];
  84. [session release];
  85. [input release];
  86. [device release];
  87. [fileOutput release];
  88. [imageOutput release];
  89. [callbackDelegate release];
  90. }
  91. void resetFile()
  92. {
  93. [session removeOutput: fileOutput];
  94. [fileOutput release];
  95. fileOutput = [[QTCaptureMovieFileOutput alloc] init];
  96. [fileOutput setDelegate: callbackDelegate];
  97. }
  98. void addListener (CameraImageListener* listenerToAdd)
  99. {
  100. const ScopedLock sl (listenerLock);
  101. if (listeners.size() == 0)
  102. [session addOutput: imageOutput error: nil];
  103. listeners.addIfNotAlreadyThere (listenerToAdd);
  104. }
  105. void removeListener (CameraImageListener* listenerToRemove)
  106. {
  107. const ScopedLock sl (listenerLock);
  108. listeners.removeValue (listenerToRemove);
  109. if (listeners.size() == 0)
  110. [session removeOutput: imageOutput];
  111. }
  112. static void drawNSBitmapIntoJuceImage (Image& dest, NSBitmapImageRep* source)
  113. {
  114. const ScopedAutoReleasePool pool;
  115. int lineStride, pixelStride;
  116. uint8* pixels = dest.lockPixelDataReadWrite (0, 0, dest.getWidth(), dest.getHeight(),
  117. lineStride, pixelStride);
  118. NSBitmapImageRep* rep = [[NSBitmapImageRep alloc]
  119. initWithBitmapDataPlanes: &pixels
  120. pixelsWide: dest.getWidth()
  121. pixelsHigh: dest.getHeight()
  122. bitsPerSample: 8
  123. samplesPerPixel: pixelStride
  124. hasAlpha: dest.hasAlphaChannel()
  125. isPlanar: NO
  126. colorSpaceName: NSCalibratedRGBColorSpace
  127. bitmapFormat: (NSBitmapFormat) 0
  128. bytesPerRow: lineStride
  129. bitsPerPixel: pixelStride * 8];
  130. [NSGraphicsContext saveGraphicsState];
  131. [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithBitmapImageRep: rep]];
  132. [source drawAtPoint: NSZeroPoint];
  133. [[NSGraphicsContext currentContext] flushGraphics];
  134. [NSGraphicsContext restoreGraphicsState];
  135. uint8* start = pixels;
  136. for (int h = dest.getHeight(); --h >= 0;)
  137. {
  138. uint8* p = start;
  139. start += lineStride;
  140. for (int i = dest.getWidth(); --i >= 0;)
  141. {
  142. #if JUCE_BIG_ENDIAN
  143. const uint8 oldp3 = p[3];
  144. const uint8 oldp1 = p[1];
  145. p[3] = p[0];
  146. p[0] = oldp1;
  147. p[1] = p[2];
  148. p[2] = oldp3;
  149. #else
  150. const uint8 oldp0 = p[0];
  151. p[0] = p[2];
  152. p[2] = oldp0;
  153. #endif
  154. p += pixelStride;
  155. }
  156. }
  157. dest.releasePixelDataReadWrite (pixels);
  158. }
  159. void callListeners (NSBitmapImageRep* bitmap)
  160. {
  161. Image image (Image::ARGB, [bitmap size].width, [bitmap size].height, false);
  162. drawNSBitmapIntoJuceImage (image, bitmap);
  163. const ScopedLock sl (listenerLock);
  164. for (int i = listeners.size(); --i >= 0;)
  165. {
  166. CameraImageListener* l = (CameraImageListener*) listeners[i];
  167. if (l != 0)
  168. l->imageReceived (image);
  169. }
  170. }
  171. QTCaptureDevice* device;
  172. QTCaptureDeviceInput* input;
  173. QTCaptureSession* session;
  174. QTCaptureMovieFileOutput* fileOutput;
  175. QTCaptureDecompressedVideoOutput* imageOutput;
  176. QTCaptureCallbackDelegate* callbackDelegate;
  177. String openingError;
  178. VoidArray listeners;
  179. CriticalSection listenerLock;
  180. };
  181. END_JUCE_NAMESPACE
  182. @implementation QTCaptureCallbackDelegate
  183. - (QTCaptureCallbackDelegate*) initWithOwner: (CameraDevice*) owner_
  184. internalDev: (QTCameraDeviceInteral*) d
  185. {
  186. [super init];
  187. owner = owner_;
  188. internal = d;
  189. firstRecordedTime = 0;
  190. return self;
  191. }
  192. - (void) dealloc
  193. {
  194. delete firstRecordedTime;
  195. [super dealloc];
  196. }
  197. - (void) captureOutput: (QTCaptureOutput*) captureOutput
  198. didOutputVideoFrame: (CVImageBufferRef) videoFrame
  199. withSampleBuffer: (QTSampleBuffer*) sampleBuffer
  200. fromConnection: (QTCaptureConnection*) connection
  201. {
  202. const ScopedAutoReleasePool pool;
  203. CIImage* image = [CIImage imageWithCVImageBuffer: videoFrame];
  204. NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] initWithCIImage: image] autorelease];
  205. internal->callListeners (bitmap);
  206. }
  207. - (void) captureOutput: (QTCaptureFileOutput*) captureOutput
  208. didOutputSampleBuffer: (QTSampleBuffer*) sampleBuffer
  209. fromConnection: (QTCaptureConnection*) connection
  210. {
  211. if (firstRecordedTime == 0)
  212. firstRecordedTime = new Time (Time::getCurrentTime());
  213. }
  214. @end
  215. BEGIN_JUCE_NAMESPACE
  216. //==============================================================================
  217. class QTCaptureViewerComp : public NSViewComponent
  218. {
  219. public:
  220. QTCaptureViewerComp (CameraDevice* const cameraDevice, QTCameraDeviceInteral* const internal)
  221. {
  222. const ScopedAutoReleasePool pool;
  223. captureView = [[QTCaptureView alloc] init];
  224. [captureView setCaptureSession: internal->session];
  225. setSize (640, 480); // xxx need to somehow get the movie size - how?
  226. setView (captureView);
  227. }
  228. ~QTCaptureViewerComp()
  229. {
  230. setView (0);
  231. [captureView setCaptureSession: nil];
  232. [captureView release];
  233. }
  234. QTCaptureView* captureView;
  235. };
  236. //==============================================================================
  237. CameraDevice::CameraDevice (const String& name_, int index)
  238. : name (name_)
  239. {
  240. isRecording = false;
  241. QTCameraDeviceInteral* d = new QTCameraDeviceInteral (this, index);
  242. internal = d;
  243. }
  244. CameraDevice::~CameraDevice()
  245. {
  246. stopRecording();
  247. delete (QTCameraDeviceInteral*) internal;
  248. internal = 0;
  249. }
  250. Component* CameraDevice::createViewerComponent()
  251. {
  252. return new QTCaptureViewerComp (this, (QTCameraDeviceInteral*) internal);
  253. }
  254. const String CameraDevice::getFileExtension()
  255. {
  256. return ".mov";
  257. }
  258. void CameraDevice::startRecordingToFile (const File& file)
  259. {
  260. stopRecording();
  261. QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal;
  262. deleteAndZero (d->callbackDelegate->firstRecordedTime);
  263. file.deleteFile();
  264. [d->fileOutput recordToOutputFileURL: [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())]];
  265. [d->session addOutput: d->fileOutput error: nil];
  266. isRecording = true;
  267. }
  268. const Time CameraDevice::getTimeOfFirstRecordedFrame() const
  269. {
  270. QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal;
  271. if (d->callbackDelegate->firstRecordedTime != 0)
  272. return *d->callbackDelegate->firstRecordedTime;
  273. return Time();
  274. }
  275. void CameraDevice::stopRecording()
  276. {
  277. if (isRecording)
  278. {
  279. QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal;
  280. d->resetFile();
  281. isRecording = false;
  282. }
  283. }
  284. void CameraDevice::addListener (CameraImageListener* listenerToAdd)
  285. {
  286. QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal;
  287. if (listenerToAdd != 0)
  288. d->addListener (listenerToAdd);
  289. }
  290. void CameraDevice::removeListener (CameraImageListener* listenerToRemove)
  291. {
  292. QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal;
  293. if (listenerToRemove != 0)
  294. d->removeListener (listenerToRemove);
  295. }
  296. //==============================================================================
  297. const StringArray CameraDevice::getAvailableDevices()
  298. {
  299. const ScopedAutoReleasePool pool;
  300. StringArray results;
  301. NSArray* devs = [QTCaptureDevice inputDevicesWithMediaType: QTMediaTypeVideo];
  302. for (int i = 0; i < [devs count]; ++i)
  303. {
  304. QTCaptureDevice* dev = (QTCaptureDevice*) [devs objectAtIndex: i];
  305. results.add (nsStringToJuce ([dev localizedDisplayName]));
  306. }
  307. return results;
  308. }
  309. CameraDevice* CameraDevice::openDevice (int index,
  310. int minWidth, int minHeight,
  311. int maxWidth, int maxHeight)
  312. {
  313. CameraDevice* d = new CameraDevice (getAvailableDevices() [index], index);
  314. if (((QTCameraDeviceInteral*) (d->internal))->openingError.isEmpty())
  315. return d;
  316. delete d;
  317. return 0;
  318. }
  319. #endif