2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using System.Diagnostics;
22 using System.Runtime.InteropServices;
23 using System.Threading;
24 using Native = Interop.Camera;
26 namespace Tizen.Multimedia
28 static internal class CameraLog
30 internal const string Tag = "Tizen.Multimedia.Camera";
31 internal const string Enter = "[Enter]";
32 internal const string Leave = "[Leave]";
36 /// This camera class provides methods to capture photos and supports setting up notifications
37 /// for state changes of capturing, previewing, focusing, and informing about the resolution and the binary format,
38 /// and functions for picture manipulations like sepia, negative, and many more.
39 /// It also notifies you when a significant picture parameter changes, (For example, focus).
41 /// <since_tizen> 3 </since_tizen>
42 /// <feature> http://tizen.org/feature/camera </feature>
43 public partial class Camera : IDisposable, IDisplayable<CameraError>
45 private IntPtr _handle = IntPtr.Zero;
46 private bool _disposed = false;
47 private CameraState _state = CameraState.None;
48 PinnedPreviewBuffer<byte> _previewBuffer;
51 /// Initializes a new instance of the <see cref="Camera"/> class.
53 /// <param name="device">The camera device to access.</param>
54 /// <exception cref="ArgumentException">Invalid CameraDevice type.</exception>
55 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
56 /// <exception cref="NotSupportedException">The camera feature is not supported.</exception>
57 /// <since_tizen> 3 </since_tizen>
58 /// <feature> http://tizen.org/feature/camera </feature>
59 public Camera(CameraDevice device)
61 ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
63 CreateCameraDevice(device);
69 /// Initializes a new instance of the <see cref="Camera"/> class.
71 /// <remarks>CameraDevice and Type will be selected internally by CameraDeviceManager.</remarks>
72 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
73 /// <exception cref="NotSupportedException">The camera feature is not supported.</exception>
74 /// <since_tizen> 9 </since_tizen>
75 /// <feature> http://tizen.org/feature/camera </feature>
76 [EditorBrowsable(EditorBrowsableState.Never)]
77 public Camera() : this(CameraDevice.Default)
81 private void Initialize()
83 Capabilities = new CameraCapabilities(this);
84 Settings = new CameraSettings(this);
85 DisplaySettings = new CameraDisplaySettings(this);
89 SetState(CameraState.Created);
92 private void CreateCameraDevice(CameraDevice device)
94 CameraDeviceType cameraDeviceType = CameraDeviceType.BuiltIn;
95 CameraDevice cameraDevice = device;
97 if (CameraDeviceManager.IsSupported || device == CameraDevice.Default)
99 var deviceInfo = GetDeviceInformation();
100 if (!deviceInfo.Any())
102 throw new InvalidOperationException("CDM is supported but, there's no available camera device.");
105 cameraDeviceType = deviceInfo.First().Type;
106 cameraDevice = deviceInfo.First().Device;
107 Log.Debug(CameraLog.Tag, $"Type:[{cameraDeviceType}], Device:[{cameraDevice}]");
110 CreateNativeCameraDevice(cameraDeviceType, cameraDevice);
113 private IEnumerable<CameraDeviceInformation> GetDeviceInformation()
115 using (var cameraDeviceManager = new CameraDeviceManager())
117 return cameraDeviceManager.GetDeviceInformation();
121 private void CreateNativeCameraDevice(CameraDeviceType type, CameraDevice device)
123 if (type == CameraDeviceType.BuiltIn || type == CameraDeviceType.Usb)
125 Native.Create(device, out _handle).
126 ThrowIfFailed($"Failed to create {type} camera");
130 Native.CreateNetworkCamera(device, out _handle).
131 ThrowIfFailed($"Failed to create {type} camera");
136 /// Finalizes an instance of the Camera class.
144 /// Gets the native handle of the camera.
146 /// <since_tizen> 3 </since_tizen>
147 /// <feature> http://tizen.org/feature/camera </feature>
148 public IntPtr Handle => GetHandle();
150 internal IntPtr GetHandle()
152 ValidateNotDisposed();
156 #region Dispose support
158 /// Releases the unmanaged resources used by the camera.
160 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
161 /// <since_tizen> 3 </since_tizen>
162 protected virtual void Dispose(bool disposing)
168 // to be used if there are any other disposable objects
169 _previewBuffer?.Dispose();
172 if (_handle != IntPtr.Zero)
174 Native.Destroy(_handle);
175 _handle = IntPtr.Zero;
183 /// Releases all resources used by the camera.
185 /// <since_tizen> 3 </since_tizen>
186 /// <feature> http://tizen.org/feature/camera </feature>
187 public void Dispose()
189 ReplaceDisplay(null);
191 GC.SuppressFinalize(this);
194 internal void ValidateNotDisposed()
198 Log.Error(CameraLog.Tag, "Camera handle is disposed.");
199 throw new ObjectDisposedException(nameof(Camera));
202 #endregion Dispose support
204 internal void ValidateState(params CameraState[] required)
206 ValidateNotDisposed();
208 Debug.Assert(required.Length > 0);
210 var curState = _state;
211 if (!required.Contains(curState))
213 throw new InvalidOperationException($"The camera is not in a valid state. " +
214 $"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
218 internal void SetState(CameraState state)
224 /// Changes the camera device.
226 /// <param name="device">The hardware camera to access.</param>
227 /// <since_tizen> 3 </since_tizen>
228 /// <feature> http://tizen.org/feature/camera </feature>
230 /// If display reuse is set using <see cref="DisplayReuseHint"/>
231 /// before stopping the preview, the display will be reused and last frame on the display
232 /// can be kept even though camera device is changed.
233 /// The camera must be in the <see cref="CameraState.Created"/>.
235 /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
236 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
237 /// <exception cref="NotSupportedException">In case of the ChangeDevice feature is not supported.</exception>
238 /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
239 public void ChangeDevice(CameraDevice device)
241 ValidateState(CameraState.Created);
242 ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
244 Native.ChangeDevice(_handle, device).ThrowIfFailed("Failed to change the camera device");
248 /// Gets the device state.
250 /// <param name="device">The device to get the state.</param>
251 /// <returns>Returns the state of the camera device.</returns>
252 /// <since_tizen> 4 </since_tizen>
253 /// <feature> http://tizen.org/feature/camera </feature>
254 /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
255 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
256 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
257 public static CameraDeviceState GetDeviceState(CameraDevice device)
259 ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
261 Native.GetDeviceState(device, out var val).ThrowIfFailed("Failed to get the camera device state.");
267 /// Gets the flash state.
269 /// <param name="device">The device to get the state.</param>
270 /// <returns>Returns the flash state of the camera device.</returns>
271 /// <since_tizen> 3 </since_tizen>
272 /// <feature> http://tizen.org/feature/camera </feature>
273 /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
274 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
275 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
276 public static CameraFlashState GetFlashState(CameraDevice device)
278 ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
280 Native.GetFlashState(device, out var val).ThrowIfFailed("Failed to get camera flash state");
286 /// Starts capturing and drawing preview frames on the screen.
287 /// The display property must be set using <see cref="Display"/> before using this method.
288 /// If needed set fps <see cref="CameraSettings.PreviewFps"/>, preview resolution
289 /// <see cref="CameraSettings.PreviewResolution"/>, or preview format <see cref="CameraSettings.PreviewPixelFormat"/>
290 /// before using this method.
291 /// The camera must be in the <see cref="CameraState.Created"/> or the <see cref="CameraState.Captured"/> state.
293 /// <since_tizen> 3 </since_tizen>
294 /// <privilege> http://tizen.org/privilege/camera </privilege>
295 /// <feature> http://tizen.org/feature/camera </feature>
296 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
297 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
298 /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
299 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
300 public void StartPreview()
302 ValidateState(CameraState.Created, CameraState.Captured);
304 Native.StartPreview(_handle).ThrowIfFailed("Failed to start the camera preview.");
306 // Update by StateChangedCallback can be delayed for dozens of milliseconds.
307 SetState(CameraState.Preview);
311 /// Stops capturing and drawing preview frames on the screen.
312 /// The camera must be in the <see cref="CameraState.Preview"/> state.
314 /// <since_tizen> 3 </since_tizen>
315 /// <privilege> http://tizen.org/privilege/camera </privilege>
316 /// <feature> http://tizen.org/feature/camera </feature>
317 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
318 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
319 /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
320 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
321 public void StopPreview()
323 ValidateState(CameraState.Preview);
325 Native.StopPreview(_handle).ThrowIfFailed("Failed to stop the camera preview.");
327 SetState(CameraState.Created);
331 /// Starts capturing of still images.
332 /// EventHandler must be set for capturing using <see cref="Capturing"/>
333 /// and for completed using <see cref="CaptureCompleted"/> before calling this method.
334 /// The camera must be in the <see cref="CameraState.Preview"/> state.
336 /// <since_tizen> 3 </since_tizen>
337 /// <privilege> http://tizen.org/privilege/camera </privilege>
338 /// <feature> http://tizen.org/feature/camera </feature>
340 /// This function causes the transition of the camera state from capturing to captured
341 /// automatically and the corresponding EventHandlers will be invoked.
342 /// The preview should be restarted by calling the <see cref="StartPreview"/> method after capture is completed.
344 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
345 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
346 /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
347 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
348 public void StartCapture()
350 ValidateState(CameraState.Preview);
352 Native.StartCapture(_handle, _capturingCallback, _captureCompletedCallback).
353 ThrowIfFailed("Failed to start the camera capture.");
355 SetState(CameraState.Capturing);
359 /// Starts continuously capturing still images.
360 /// EventHandler must be set for capturing using <see cref="Capturing"/>
361 /// and for completed using <see cref="CaptureCompleted"/> before calling this method.
362 /// The camera must be in the <see cref="CameraState.Preview"/> state.
364 /// <param name="count">The number of still images.</param>
365 /// <param name="interval">The interval of the capture(milliseconds).</param>
366 /// <param name="cancellationToken">The cancellation token to cancel capturing.</param>
367 /// <seealso cref="CancellationToken"/>
368 /// <since_tizen> 3 </since_tizen>
369 /// <privilege> http://tizen.org/privilege/camera </privilege>
370 /// <feature> http://tizen.org/feature/camera </feature>
372 /// If this is not supported, zero shutter lag occurs. The capture resolution could be
373 /// changed to the preview resolution. This function causes the transition of the camera state
374 /// from capturing to captured automatically and the corresponding Eventhandlers will be invoked.
375 /// Each captured image will be delivered through Eventhandler set using the <see cref="Capturing"/> event.
376 /// The preview should be restarted by calling the <see cref="StartPreview"/> method after capture is completed.
378 /// <exception cref="ArgumentOutOfRangeException">In case of invalid parameters.</exception>
379 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
380 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
381 /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
382 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
383 public void StartCapture(int count, int interval, CancellationToken cancellationToken)
385 ValidateState(CameraState.Preview);
389 throw new ArgumentOutOfRangeException(nameof(count), count, $"{nameof(count)} should be greater than one.");
394 throw new ArgumentOutOfRangeException(nameof(interval), interval, $"{nameof(interval)} should be greater than or equal to zero.");
397 //Handle CancellationToken
398 if (cancellationToken != CancellationToken.None)
400 cancellationToken.Register(() =>
402 Native.StopContinuousCapture(_handle).ThrowIfFailed("Failed to cancel the continuous capture");
403 SetState(CameraState.Captured);
407 Native.StartContinuousCapture(_handle, count, interval, _capturingCallback, _captureCompletedCallback).
408 ThrowIfFailed("Failed to start the continuous capture.");
410 SetState(CameraState.Capturing);
414 /// Starts camera auto-focusing, asynchronously.
415 /// The camera must be in the <see cref="CameraState.Preview"/> or the <see cref="CameraState.Captured"/> state.
417 /// <param name="continuous">Continuous auto focus.</param>
418 /// <since_tizen> 3 </since_tizen>
419 /// <privilege> http://tizen.org/privilege/camera </privilege>
420 /// <feature> http://tizen.org/feature/camera </feature>
422 /// If continuous status is true, the camera continuously tries to focus.
424 /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
425 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
426 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
427 /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
428 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
429 public void StartFocusing(bool continuous)
431 ValidateState(CameraState.Preview, CameraState.Captured);
433 Native.StartFocusing(_handle, continuous).ThrowIfFailed("Failed to cancel the camera focus.");
437 /// Stops camera auto focusing.
438 /// The camera must be in the <see cref="CameraState.Preview"/> or the <see cref="CameraState.Captured"/> state.
440 /// <since_tizen> 3 </since_tizen>
441 /// <privilege> http://tizen.org/privilege/camera </privilege>
442 /// <feature> http://tizen.org/feature/camera </feature>
443 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
444 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
445 /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
446 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
447 public void StopFocusing()
449 ValidateState(CameraState.Preview, CameraState.Captured);
451 Native.CancelFocusing(_handle).ThrowIfFailed("Failed to cancel the camera focus.");
455 /// Starts face detection.
456 /// The camera must be in the <see cref="CameraState.Preview"/> state.
458 /// <since_tizen> 3 </since_tizen>
459 /// <privilege> http://tizen.org/privilege/camera </privilege>
460 /// <feature> http://tizen.org/feature/camera </feature>
462 /// This should be called after <see cref="StartPreview"/> is started.
463 /// The Eventhandler set using <see cref="FaceDetected"/> is invoked when the face is detected in the preview frame.
464 /// Internally, it starts continuously focus and focusing on the detected face.
466 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
467 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
468 /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
469 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
470 public void StartFaceDetection()
472 ValidateState(CameraState.Preview);
474 _faceDetectedCallback = (IntPtr faces, int count, IntPtr userData) =>
476 var result = new List<FaceDetectionData>();
477 IntPtr current = faces;
479 for (int i = 0; i < count; i++)
481 result.Add(new FaceDetectionData(current));
482 current = IntPtr.Add(current, Marshal.SizeOf<Native.DetectedFaceStruct>());
485 FaceDetected?.Invoke(this, new FaceDetectedEventArgs(result));
488 Native.StartFaceDetection(_handle, _faceDetectedCallback).
489 ThrowIfFailed("Failed to start face detection");
493 /// Stops face detection.
495 /// <since_tizen> 3 </since_tizen>
496 /// <privilege> http://tizen.org/privilege/camera </privilege>
497 /// <feature> http://tizen.org/feature/camera </feature>
498 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
499 /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
500 /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
501 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
502 public void StopFaceDetection()
504 ValidateNotDisposed();
506 if (_faceDetectedCallback == null)
508 throw new InvalidOperationException("The face detection is not started.");
511 Native.StopFaceDetection(_handle).ThrowIfFailed("Failed to stop the face detection.");
513 _faceDetectedCallback = null;