[Camera] Add Camera feature check code
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Camera / Camera / Camera.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 using System;
18 using System.Collections.Generic;
19 using System.Diagnostics;
20 using System.Linq;
21 using System.Runtime.InteropServices;
22 using System.Threading;
23 using static Interop;
24 using Native = Interop.Camera;
25
26 namespace Tizen.Multimedia
27 {
28     static internal class CameraLog
29     {
30         internal const string Tag = "Tizen.Multimedia.Camera";
31         internal const string Enter = "[Enter]";
32         internal const string Leave = "[Leave]";
33     }
34
35     /// <summary>
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).
40     /// </summary>
41     /// <since_tizen> 3 </since_tizen>
42     public class Camera : IDisposable, IDisplayable<CameraError>
43     {
44         private IntPtr _handle = IntPtr.Zero;
45         private bool _disposed = false;
46         private CameraState _state = CameraState.None;
47
48         /// <summary>
49         /// Initializes a new instance of the <see cref="Camera"/> class.
50         /// </summary>
51         /// <feature>http://tizen.org/feature/camera</feature>
52         /// <since_tizen> 3 </since_tizen>
53         /// <param name="device">The camera device to access.</param>
54         public Camera(CameraDevice device)
55         {
56             if (!Features.IsSupported(CameraFeatures.Camera))
57             {
58                 throw new NotSupportedException("Camera feature is not supported.");
59             }
60
61             CameraErrorFactory.ThrowIfError(Native.Create(device, out _handle),
62                 "Failed to create camera instance");
63
64             Capabilities = new CameraCapabilities(this);
65             Settings = new CameraSettings(this);
66             DisplaySettings = new CameraDisplaySettings(this);
67
68             RegisterCallbacks();
69
70             SetState(CameraState.Created);
71         }
72
73         /// <summary>
74         /// Finalizes an instance of the Camera class.
75         /// </summary>
76         ~Camera()
77         {
78             Dispose(false);
79         }
80
81         /// <summary>
82         /// Gets the native handle of the camera.
83         /// </summary>
84         /// <since_tizen> 3 </since_tizen>
85         public IntPtr Handle => GetHandle();
86
87         internal IntPtr GetHandle()
88         {
89             ValidateNotDisposed();
90             return _handle;
91         }
92
93         #region Dispose support
94         /// <summary>
95         /// Releases the unmanaged resources used by the camera.
96         /// </summary>
97         /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
98         /// <since_tizen> 3 </since_tizen>
99         protected virtual void Dispose(bool disposing)
100         {
101             if (!_disposed)
102             {
103                 if (disposing)
104                 {
105                     // to be used if there are any other disposable objects
106                 }
107
108                 if (_handle != IntPtr.Zero)
109                 {
110                     Native.Destroy(_handle);
111                     _handle = IntPtr.Zero;
112                 }
113
114                 _disposed = true;
115             }
116         }
117
118         /// <summary>
119         /// Releases all resources used by the camera.
120         /// </summary>
121         /// <since_tizen> 3 </since_tizen>
122         public void Dispose()
123         {
124             ReplaceDisplay(null);
125             Dispose(true);
126             GC.SuppressFinalize(this);
127         }
128
129         internal void ValidateNotDisposed()
130         {
131             if (_disposed)
132             {
133                 Log.Error(CameraLog.Tag, "Camera handle is disposed.");
134                 throw new ObjectDisposedException(nameof(Camera));
135             }
136         }
137         #endregion Dispose support
138
139         #region Check camera state
140         internal void ValidateState(params CameraState[] required)
141         {
142             ValidateNotDisposed();
143
144             Debug.Assert(required.Length > 0);
145
146             var curState = _state;
147             if (!required.Contains(curState))
148             {
149                 throw new InvalidOperationException($"The camera is not in a valid state. " +
150                     $"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
151             }
152         }
153
154         internal void SetState(CameraState state)
155         {
156             _state = state;
157         }
158         #endregion Check camera state
159
160         #region EventHandlers
161         /// <summary>
162         /// An event that occurs when the camera interrupt is started by the policy.
163         /// </summary>
164         /// <since_tizen> 4 </since_tizen>
165         public event EventHandler<CameraInterruptStartedEventArgs> InterruptStarted;
166         private Native.InterruptStartedCallback _interruptStartedCallback;
167
168         /// <summary>
169         /// An event that occurs when an camera is interrupted by the policy.
170         /// </summary>
171         /// <since_tizen> 3 </since_tizen>
172         public event EventHandler<CameraInterruptedEventArgs> Interrupted;
173         private Native.InterruptedCallback _interruptedCallback;
174
175         /// <summary>
176         /// An event that occurs when there is an asynchronous error.
177         /// </summary>
178         /// <since_tizen> 3 </since_tizen>
179         public event EventHandler<CameraErrorOccurredEventArgs> ErrorOccurred;
180         private Native.ErrorCallback _errorCallback;
181
182         /// <summary>
183         /// An event that occurs when the auto focus state is changed.
184         /// </summary>
185         /// <since_tizen> 3 </since_tizen>
186         public event EventHandler<CameraFocusStateChangedEventArgs> FocusStateChanged;
187         private Native.FocusStateChangedCallback _focusStateChangedCallback;
188
189         /// <summary>
190         /// An event that occurs when a face is detected in the preview frame.
191         /// </summary>
192         /// <since_tizen> 3 </since_tizen>
193         public event EventHandler<FaceDetectedEventArgs> FaceDetected;
194         private Native.FaceDetectedCallback _faceDetectedCallback;
195
196         /// <summary>
197         /// An event that occurs during capture of an image.
198         /// </summary>
199         /// <since_tizen> 3 </since_tizen>
200         public event EventHandler<CameraCapturingEventArgs> Capturing;
201         private Native.CapturingCallback _capturingCallback;
202
203         /// <summary>
204         /// An event that occurs after the capture of the image.
205         /// </summary>
206         /// <since_tizen> 3 </since_tizen>
207         public event EventHandler<EventArgs> CaptureCompleted;
208         private Native.CaptureCompletedCallback _captureCompletedCallback;
209
210         private Native.HdrCaptureProgressCallback _hdrCaptureProgressCallback;
211         private event EventHandler<HdrCaptureProgressEventArgs> _hdrCaptureProgress;
212         private object _hdrCaptureProgressEventLock = new object();
213
214         /// <summary>
215         /// An event that occurs when there is a change in the HDR capture progress.
216         /// Checks whether the <see cref="CameraCapabilities.IsHdrCaptureSupported"/> is supported or not before adding this EventHandler.
217         /// </summary>
218         /// <since_tizen> 3 </since_tizen>
219         /// <exception cref="NotSupportedException">In case of HDR feature is not supported.</exception>
220         public event EventHandler<HdrCaptureProgressEventArgs> HdrCaptureProgress
221         {
222             add
223             {
224                 lock (_hdrCaptureProgressEventLock)
225                 {
226                     if (_hdrCaptureProgress == null)
227                     {
228                         RegisterHdrCaptureProgress();
229                     }
230
231                     _hdrCaptureProgress += value;
232                 }
233             }
234
235             remove
236             {
237                 lock (_hdrCaptureProgressEventLock)
238                 {
239                     _hdrCaptureProgress -= value;
240
241                     if (_hdrCaptureProgress == null)
242                     {
243                         UnregisterHdrCaptureProgress();
244                     }
245                 }
246             }
247         }
248
249         /// <summary>
250         /// An event that occurs when the camera state is changed.
251         /// </summary>
252         /// <since_tizen> 3 </since_tizen>
253         public event EventHandler<CameraStateChangedEventArgs> StateChanged;
254         private Native.StateChangedCallback _stateChangedCallback;
255
256         private static Native.DeviceStateChangedCallback _deviceStateChangedCallback;
257         private static event EventHandler<CameraDeviceStateChangedEventArgs> _deviceStateChanged;
258         private static object _deviceStateChangedEventLock = new object();
259         private static int _deviceStateCallbackId;
260
261         /// <summary>
262         /// An event that occurs after the <see cref="CameraDeviceState"/> is changed.
263         /// </summary>
264         /// <since_tizen> 3 </since_tizen>
265         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
266         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
267         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
268         public static event EventHandler<CameraDeviceStateChangedEventArgs> DeviceStateChanged
269         {
270             add
271             {
272                 lock (_deviceStateChangedEventLock)
273                 {
274                     if (_deviceStateChanged == null)
275                     {
276                         RegisterDeviceStateChangedCallback();
277                     }
278
279                     _deviceStateChanged += value;
280                 }
281             }
282
283             remove
284             {
285                 lock (_deviceStateChangedEventLock)
286                 {
287                     _deviceStateChanged -= value;
288
289                     if (_deviceStateChanged == null)
290                     {
291                         UnregisterDeviceStateChangedCallback();
292                     }
293                 }
294             }
295         }
296
297         private Native.PreviewCallback _previewCallback;
298         private event EventHandler<PreviewEventArgs> _preview;
299         private object _previewEventLock = new object();
300         /// <summary>
301         /// An event that occurs once per frame when previewing.
302         /// Preview callback is registered when an user adds a callback explicitly to avoid useless P/Invoke.
303         /// </summary>
304         /// <since_tizen> 3 </since_tizen>
305         public event EventHandler<PreviewEventArgs> Preview
306         {
307             add
308             {
309                 lock (_previewEventLock)
310                 {
311                     if (_preview == null)
312                     {
313                         RegisterPreviewCallback();
314                     }
315
316                     _preview += value;
317                 }
318             }
319
320             remove
321             {
322                 lock (_previewEventLock)
323                 {
324                     _preview -= value;
325
326                     if (_preview == null)
327                     {
328                         UnregisterPreviewCallback();
329                     }
330                 }
331             }
332         }
333
334         private Native.MediaPacketPreviewCallback _mediaPacketPreviewCallback;
335         private EventHandler<MediaPacketPreviewEventArgs> _mediaPacketPreview;
336         private object _mediaPacketPreviewEventLock = new object();
337
338         /// <summary>
339         /// An event that occurs once per frame when previewing.
340         /// Preview callback is registered when an user adds a callback explicitly to avoid useless P/Invoke.
341         /// </summary>
342         /// <since_tizen> 3 </since_tizen>
343         public event EventHandler<MediaPacketPreviewEventArgs> MediaPacketPreview
344         {
345             add
346             {
347                 lock (_mediaPacketPreviewEventLock)
348                 {
349                     if (_mediaPacketPreview == null)
350                     {
351                         RegisterMediaPacketPreviewCallback();
352                     }
353
354                     _mediaPacketPreview += value;
355                 }
356             }
357
358             remove
359             {
360                 lock (_mediaPacketPreviewEventLock)
361                 {
362                     _mediaPacketPreview -= value;
363
364                     if (_mediaPacketPreview == null)
365                     {
366                         UnregisterMediaPacketPreviewCallback();
367                     }
368                 }
369             }
370         }
371         #endregion EventHandlers
372
373         #region Properties
374         /// <summary>
375         /// Gets or sets the various camera settings.
376         /// </summary>
377         /// <since_tizen> 4 </since_tizen>
378         public CameraSettings Settings { get; }
379
380         /// <summary>
381         /// Gets the various camera capabilities.
382         /// </summary>
383         /// <since_tizen> 4 </since_tizen>
384         public CameraCapabilities Capabilities { get; }
385
386         /// <summary>
387         /// Get/set various camera display properties.
388         /// </summary>
389         /// <since_tizen> 3 </since_tizen>
390         public CameraDisplaySettings DisplaySettings { get; }
391
392         private Display _display;
393
394         private CameraError SetDisplay(Display display)
395         {
396             if (display == null)
397             {
398                 return CameraDisplay.SetTarget(GetHandle(), DisplayType.None, IntPtr.Zero);
399             }
400
401             return display.ApplyTo(this);
402         }
403
404         private void ReplaceDisplay(Display newDisplay)
405         {
406             _display?.SetOwner(null);
407             _display = newDisplay;
408             _display?.SetOwner(this);
409         }
410
411         /// <summary>
412         /// Sets or gets the display type and handle to show preview images.
413         /// The camera must be in the <see cref="CameraState.Created"/> state.
414         /// </summary>
415         /// <since_tizen> 3 </since_tizen>
416         /// <remarks>
417         /// This must be set before the StartPreview() method.
418         /// In custom ROI display mode, DisplayRoiArea property must be set before calling this method.
419         /// </remarks>
420         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
421         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
422         /// <exception cref="ObjectDisposedException" > The camera already has been disposed of.</exception>
423         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
424         public Display Display
425         {
426             get
427             {
428                 return _display;
429             }
430
431             set
432             {
433                 ValidateState(CameraState.Created);
434
435                 if (value?.Owner != null)
436                 {
437                     if (ReferenceEquals(this, value.Owner))
438                     {
439                         return;
440                     }
441
442                     throw new ArgumentException("The display has already been assigned to another.");
443                 }
444
445                 CameraErrorFactory.ThrowIfError(SetDisplay(value), "Failed to set the camera display");
446
447                 ReplaceDisplay(value);
448             }
449         }
450
451         CameraError IDisplayable<CameraError>.ApplyEvasDisplay(DisplayType type, ElmSharp.EvasObject evasObject)
452         {
453             Debug.Assert(_disposed == false);
454             ValidationUtil.ValidateEnum(typeof(DisplayType), type, nameof(type));
455
456             return CameraDisplay.SetTarget(GetHandle(), type, evasObject);
457         }
458
459         CameraError IDisplayable<CameraError>.ApplyEcoreWindow(IntPtr windowHandle)
460         {
461             throw new NotSupportedException("Camera does not support NUI.Window display.");
462         }
463
464         /// <summary>
465         /// Gets the state of the camera.
466         /// </summary>
467         /// <since_tizen> 3 </since_tizen>
468         /// <value> None, Created, Preview, Capturing, Captured.</value>
469         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
470         public CameraState State
471         {
472             get
473             {
474                 ValidateNotDisposed();
475
476                 CameraState val = CameraState.None;
477                 CameraErrorFactory.ThrowIfError(Native.GetState(_handle, out val),
478                     "Failed to get camera state");
479
480                 return val;
481             }
482         }
483
484         /// <summary>
485         /// The hint for the display reuse.
486         /// If the hint is set to true, the display will be reused when the camera device is changed with
487         /// the ChangeDevice method.
488         /// </summary>
489         /// <since_tizen> 3 </since_tizen>
490         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
491         /// <exception cref="InvalidOperationException">An invalid state.</exception>
492         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
493         public bool DisplayReuseHint
494         {
495             get
496             {
497                 ValidateNotDisposed();
498
499                 CameraErrorFactory.ThrowIfError(Native.GetDisplayReuseHint(_handle, out bool val),
500                     "Failed to get camera display reuse hint");
501
502                 return val;
503             }
504
505             set
506             {
507                 ValidateState(CameraState.Preview);
508
509                 CameraErrorFactory.ThrowIfError(Native.SetDisplayReuseHint(_handle, value),
510                     "Failed to set display reuse hint.");
511             }
512         }
513
514         /// <summary>
515         /// Gets the facing direction of the camera module.
516         /// </summary>
517         /// <since_tizen> 3 </since_tizen>
518         /// <value>A <see cref="CameraFacingDirection"/> that specifies the facing direction of the camera device.</value>
519         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
520         public CameraFacingDirection Direction
521         {
522             get
523             {
524                 ValidateNotDisposed();
525
526                 CameraErrorFactory.ThrowIfError(Native.GetFacingDirection(_handle, out var val),
527                     "Failed to get camera direction");
528
529                 return val;
530             }
531         }
532
533         /// <summary>
534         /// Gets the camera device count.
535         /// </summary>
536         /// <since_tizen> 3 </since_tizen>
537         /// <value>This returns 2, if the device supports primary and secondary cameras.
538         /// Otherwise 1, if the device only supports primary camera.</value>
539         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
540         public int CameraCount
541         {
542             get
543             {
544                 ValidateNotDisposed();
545
546                 CameraErrorFactory.ThrowIfError(Native.GetDeviceCount(_handle, out int val),
547                     "Failed to get camera device count");
548
549                 return val;
550             }
551         }
552         #endregion Properties
553
554         #region Methods
555         /// <summary>
556         /// Changes the camera device.
557         /// </summary>
558         /// <since_tizen> 3 </since_tizen>
559         /// <param name="device">The hardware camera to access.</param>
560         /// <remarks>
561         /// If display reuse is set using <see cref="DisplayReuseHint"/>
562         /// before stopping the preview, the display will be reused and last frame on the display
563         /// can be kept even though camera device is changed.
564         /// The camera must be in the <see cref="CameraState.Created"/>.
565         /// </remarks>
566         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
567         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
568         /// <exception cref="NotSupportedException">In case of the ChangeDevice feature is not supported.</exception>
569         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
570         public void ChangeDevice(CameraDevice device)
571         {
572             ValidateState(CameraState.Created);
573             ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
574
575             CameraErrorFactory.ThrowIfError(Native.ChangeDevice(_handle, device),
576                 "Failed to change the camera device");
577         }
578
579         /// <summary>
580         /// Gets the device state.
581         /// </summary>
582         /// <since_tizen> 4 </since_tizen>
583         /// <param name="device">The device to get the state.</param>
584         /// <returns>Returns the state of the camera device.</returns>
585         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
586         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
587         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
588         public static CameraDeviceState GetDeviceState(CameraDevice device)
589         {
590             ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
591
592             CameraErrorFactory.ThrowIfError(Native.GetDeviceState(device, out var val),
593                 "Failed to get the camera device state.");
594
595             return val;
596         }
597
598         /// <summary>
599         /// Gets the flash state.
600         /// </summary>
601         /// <since_tizen> 3 </since_tizen>
602         /// <param name="device">The device to get the state.</param>
603         /// <returns>Returns the flash state of the camera device.</returns>
604         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
605         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
606         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
607         public static CameraFlashState GetFlashState(CameraDevice device)
608         {
609             ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
610
611             CameraErrorFactory.ThrowIfError(Native.GetFlashState(device, out var val),
612                 "Failed to get camera flash state");
613
614             return val;
615         }
616
617         /// <summary>
618         /// Starts capturing and drawing preview frames on the screen.
619         /// The display property must be set using <see cref="Display"/> before using this method.
620         /// If needed set fps <see cref="CameraSettings.PreviewFps"/>, preview resolution
621         /// <see cref="CameraSettings.PreviewResolution"/>, or preview format <see cref="CameraSettings.PreviewPixelFormat"/>
622         /// before using this method.
623         /// The camera must be in the <see cref="CameraState.Created"/> or the <see cref="CameraState.Captured"/> state.
624         /// </summary>
625         /// <since_tizen> 3 </since_tizen>
626         /// <privilege>
627         /// http://tizen.org/privilege/camera
628         /// </privilege>
629         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
630         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
631         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
632         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
633         public void StartPreview()
634         {
635             ValidateState(CameraState.Created, CameraState.Captured);
636
637             CameraErrorFactory.ThrowIfError(Native.StartPreview(_handle),
638                 "Failed to start the camera preview.");
639
640             // Update by StateChangedCallback can be delayed for dozens of milliseconds.
641             SetState(CameraState.Preview);
642         }
643
644         /// <summary>
645         /// Stops capturing and drawing preview frames on the screen.
646         /// The camera must be in the <see cref="CameraState.Preview"/> state.
647         /// </summary>
648         /// <since_tizen> 3 </since_tizen>
649         /// <privilege>
650         /// http://tizen.org/privilege/camera
651         /// </privilege>
652         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
653         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
654         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
655         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
656         public void StopPreview()
657         {
658             ValidateState(CameraState.Preview);
659
660             CameraErrorFactory.ThrowIfError(Native.StopPreview(_handle),
661                 "Failed to stop the camera preview.");
662
663             SetState(CameraState.Created);
664         }
665
666         /// <summary>
667         /// Starts capturing of still images.
668         /// EventHandler must be set for capturing using <see cref="Capturing"/>
669         /// and for completed using <see cref="CaptureCompleted"/> before calling this method.
670         /// The camera must be in the <see cref="CameraState.Preview"/> state.
671         /// </summary>
672         /// <since_tizen> 3 </since_tizen>
673         /// <privilege>
674         /// http://tizen.org/privilege/camera
675         /// </privilege>
676         /// <remarks>
677         /// This function causes the transition of the camera state from capturing to captured
678         /// automatically and the corresponding EventHandlers will be invoked.
679         /// The preview should be restarted by calling the <see cref="StartPreview"/> method after capture is completed.
680         /// </remarks>
681         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
682         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
683         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
684         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
685         public void StartCapture()
686         {
687             ValidateState(CameraState.Preview);
688
689             CameraErrorFactory.ThrowIfError(Native.StartCapture(_handle, _capturingCallback, _captureCompletedCallback, IntPtr.Zero),
690                 "Failed to start the camera capture.");
691
692             SetState(CameraState.Capturing);
693         }
694
695         /// <summary>
696         /// Starts continuously capturing still images.
697         /// EventHandler must be set for capturing using <see cref="Capturing"/>
698         /// and for completed using <see cref="CaptureCompleted"/> before calling this method.
699         /// The camera must be in the <see cref="CameraState.Preview"/> state.
700         /// </summary>
701         /// <since_tizen> 3 </since_tizen>
702         /// <privilege>
703         /// http://tizen.org/privilege/camera
704         /// </privilege>
705         /// <param name="count">The number of still images.</param>
706         /// <param name="interval">The interval of the capture(milliseconds).</param>
707         /// <param name="cancellationToken">The cancellation token to cancel capturing.</param>
708         /// <seealso cref="CancellationToken"/>
709         /// <remarks>
710         /// If this is not supported, zero shutter lag occurs. The capture resolution could be
711         /// changed to the preview resolution. This function causes the transition of the camera state
712         /// from capturing to captured automatically and the corresponding Eventhandlers will be invoked.
713         /// Each captured image will be delivered through Eventhandler set using the <see cref="Capturing"/> event.
714         /// The preview should be restarted by calling the <see cref="StartPreview"/> method after capture is completed.
715         /// </remarks>
716         /// <exception cref="ArgumentOutOfRangeException">In case of invalid parameters.</exception>
717         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
718         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
719         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
720         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
721         public void StartCapture(int count, int interval, CancellationToken cancellationToken)
722         {
723             ValidateState(CameraState.Preview);
724
725             if (count < 2)
726             {
727                 throw new ArgumentOutOfRangeException(nameof(count), count, $"{nameof(count)} should be greater than one.");
728             }
729
730             if (interval < 0)
731             {
732                 throw new ArgumentOutOfRangeException(nameof(interval), interval, $"{nameof(interval)} should be greater than or equal to zero.");
733             }
734
735             //Handle CancellationToken
736             if (cancellationToken != CancellationToken.None)
737             {
738                 cancellationToken.Register(() =>
739                 {
740                     CameraErrorFactory.ThrowIfError(Native.StopContinuousCapture(_handle),
741                         "Failed to cancel the continuous capture");
742                     SetState(CameraState.Captured);
743                 });
744             }
745
746             CameraErrorFactory.ThrowIfError(Native.StartContinuousCapture(_handle, count, interval,
747                 _capturingCallback, _captureCompletedCallback, IntPtr.Zero), "Failed to start the continuous capture.");
748
749             SetState(CameraState.Capturing);
750         }
751
752         /// <summary>
753         /// Starts camera auto-focusing, asynchronously.
754         /// The camera must be in the <see cref="CameraState.Preview"/> or the <see cref="CameraState.Captured"/> state.
755         /// </summary>
756         /// <since_tizen> 3 </since_tizen>
757         /// <param name="continuous">Continuous auto focus.</param>
758         /// <privilege>
759         /// http://tizen.org/privilege/camera
760         /// </privilege>
761         /// <remarks>
762         /// If continuous status is true, the camera continuously tries to focus.
763         /// </remarks>
764         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
765         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
766         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
767         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
768         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
769         public void StartFocusing(bool continuous)
770         {
771             ValidateState(CameraState.Preview, CameraState.Captured);
772
773             CameraErrorFactory.ThrowIfError(Native.StartFocusing(_handle, continuous),
774                 "Failed to cancel the camera focus.");
775         }
776
777         /// <summary>
778         /// Stops camera auto focusing.
779         /// The camera must be in the <see cref="CameraState.Preview"/> or the <see cref="CameraState.Captured"/> state.
780         /// </summary>
781         /// <since_tizen> 3 </since_tizen>
782         /// <privilege>
783         /// http://tizen.org/privilege/camera
784         /// </privilege>
785         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
786         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
787         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
788         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
789         public void StopFocusing()
790         {
791             ValidateState(CameraState.Preview, CameraState.Captured);
792
793             CameraErrorFactory.ThrowIfError(Native.CancelFocusing(_handle),
794                 "Failed to cancel the camera focus.");
795         }
796
797         /// <summary>
798         /// Starts face detection.
799         /// The camera must be in the <see cref="CameraState.Preview"/> state.
800         /// </summary>
801         /// <since_tizen> 3 </since_tizen>
802         /// <privilege>
803         /// http://tizen.org/privilege/camera
804         /// </privilege>
805         /// <remarks>
806         /// This should be called after <see cref="StartPreview"/> is started.
807         /// The Eventhandler set using <see cref="FaceDetected"/> is invoked when the face is detected in the preview frame.
808         /// Internally, it starts continuously focus and focusing on the detected face.
809         /// </remarks>
810         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
811         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
812         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
813         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
814         public void StartFaceDetection()
815         {
816             ValidateState(CameraState.Preview);
817
818             _faceDetectedCallback = (IntPtr faces, int count, IntPtr userData) =>
819             {
820                 var result = new List<FaceDetectionData>();
821                 IntPtr current = faces;
822
823                 for (int i = 0; i < count; i++)
824                 {
825                     result.Add(new FaceDetectionData(current));
826                     current = IntPtr.Add(current, Marshal.SizeOf<Native.DetectedFaceStruct>());
827                 }
828
829                 FaceDetected?.Invoke(this, new FaceDetectedEventArgs(result));
830             };
831             CameraErrorFactory.ThrowIfError(Native.StartFaceDetection(_handle, _faceDetectedCallback, IntPtr.Zero),
832                 "Failed to start face detection");
833         }
834
835         /// <summary>
836         /// Stops face detection.
837         /// </summary>
838         /// <since_tizen> 3 </since_tizen>
839         /// <privilege>
840         /// http://tizen.org/privilege/camera
841         /// </privilege>
842         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
843         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
844         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
845         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
846         public void StopFaceDetection()
847         {
848             if (_faceDetectedCallback == null)
849             {
850                 throw new InvalidOperationException("The face detection is not started.");
851             }
852
853             CameraErrorFactory.ThrowIfError(Native.StopFaceDetection(_handle),
854                 "Failed to stop the face detection.");
855
856             _faceDetectedCallback = null;
857         }
858         #endregion Methods
859
860         #region Callback registrations
861         private void RegisterCallbacks()
862         {
863             RegisterErrorCallback();
864             RegisterFocusStateChanged();
865             RegisterInterruptStartedCallback();
866             RegisterInterruptedCallback();
867             RegisterStateChangedCallback();
868
869             //Define capturing callback
870             _capturingCallback = (IntPtr main, IntPtr postview, IntPtr thumbnail, IntPtr userData) =>
871             {
872                 Capturing?.Invoke(this, new CameraCapturingEventArgs(new StillImage(main),
873                     postview == IntPtr.Zero ? null : new StillImage(postview),
874                     thumbnail == IntPtr.Zero ? null : new StillImage(thumbnail)));
875             };
876
877             //Define captureCompleted callback
878             _captureCompletedCallback = _ =>
879             {
880                 SetState(CameraState.Captured);
881                 CaptureCompleted?.Invoke(this, EventArgs.Empty);
882             };
883         }
884
885         private void RegisterInterruptStartedCallback()
886         {
887             _interruptStartedCallback = (CameraPolicy policy, CameraState state, IntPtr userData) =>
888             {
889                 InterruptStarted?.Invoke(this, new CameraInterruptStartedEventArgs(policy, state));
890             };
891             CameraErrorFactory.ThrowIfError(Native.SetInterruptStartedCallback(_handle, _interruptStartedCallback, IntPtr.Zero),
892                 "Failed to set interrupt callback");
893         }
894
895         private void RegisterInterruptedCallback()
896         {
897             _interruptedCallback = (CameraPolicy policy, CameraState previous, CameraState current, IntPtr userData) =>
898             {
899                 Interrupted?.Invoke(this, new CameraInterruptedEventArgs(policy, previous, current));
900             };
901             CameraErrorFactory.ThrowIfError(Native.SetInterruptedCallback(_handle, _interruptedCallback, IntPtr.Zero),
902                 "Failed to set interrupt callback");
903         }
904
905         private void RegisterErrorCallback()
906         {
907             _errorCallback = (CameraErrorCode error, CameraState current, IntPtr userData) =>
908             {
909                 ErrorOccurred?.Invoke(this, new CameraErrorOccurredEventArgs(error, current));
910             };
911             CameraErrorFactory.ThrowIfError(Native.SetErrorCallback(_handle, _errorCallback, IntPtr.Zero),
912                 "Setting error callback failed");
913         }
914
915         private void RegisterStateChangedCallback()
916         {
917             _stateChangedCallback = (CameraState previous, CameraState current, bool byPolicy, IntPtr _) =>
918             {
919                 SetState(current);
920                 Log.Info(CameraLog.Tag, "Camera state changed " + previous.ToString() + " -> " + current.ToString());
921                 StateChanged?.Invoke(this, new CameraStateChangedEventArgs(previous, current, byPolicy));
922             };
923             CameraErrorFactory.ThrowIfError(Native.SetStateChangedCallback(_handle, _stateChangedCallback, IntPtr.Zero),
924                 "Setting state changed callback failed");
925         }
926
927         private static void RegisterDeviceStateChangedCallback()
928         {
929             _deviceStateChangedCallback = (CameraDevice device, CameraDeviceState state, IntPtr userData) =>
930             {
931                 _deviceStateChanged?.Invoke(null, new CameraDeviceStateChangedEventArgs(device, state));
932             };
933
934             CameraErrorFactory.ThrowIfError(Native.SetDeviceStateChangedCallback(_deviceStateChangedCallback, IntPtr.Zero, out _deviceStateCallbackId),
935                 "Failed to set device state changed callback");
936
937             Log.Info(CameraLog.Tag, "add callbackId " + _deviceStateCallbackId.ToString());
938         }
939
940         private static void UnregisterDeviceStateChangedCallback()
941         {
942             CameraErrorFactory.ThrowIfError(Native.UnsetDeviceStateChangedCallback(_deviceStateCallbackId),
943                 "Unsetting device state changed callback failed");
944             _deviceStateChangedCallback = null;
945             _deviceStateCallbackId = 0;
946         }
947
948         private void RegisterFocusStateChanged()
949         {
950             _focusStateChangedCallback = (CameraFocusState state, IntPtr userData) =>
951             {
952                 FocusStateChanged?.Invoke(this, new CameraFocusStateChangedEventArgs(state));
953             };
954             CameraErrorFactory.ThrowIfError(Native.SetFocusStateChangedCallback(_handle, _focusStateChangedCallback, IntPtr.Zero),
955                 "Setting focus changed callback failed");
956         }
957
958         private void RegisterHdrCaptureProgress()
959         {
960             _hdrCaptureProgressCallback = (int percent, IntPtr userData) =>
961             {
962                 _hdrCaptureProgress?.Invoke(this, new HdrCaptureProgressEventArgs(percent));
963             };
964             CameraErrorFactory.ThrowIfError(Native.SetHdrCaptureProgressCallback(_handle, _hdrCaptureProgressCallback, IntPtr.Zero),
965                 "Setting Hdr capture progress callback failed");
966         }
967
968         private void UnregisterHdrCaptureProgress()
969         {
970             CameraErrorFactory.ThrowIfError(Native.UnsetHdrCaptureProgressCallback(_handle),
971                 "Unsetting hdr capture progress is failed");
972             _hdrCaptureProgressCallback = null;
973         }
974
975         private void RegisterPreviewCallback()
976         {
977             _previewCallback = (IntPtr frame, IntPtr userData) =>
978             {
979                 _preview?.Invoke(this, new PreviewEventArgs(new PreviewFrame(frame)));
980             };
981             CameraErrorFactory.ThrowIfError(Native.SetPreviewCallback(_handle, _previewCallback, IntPtr.Zero),
982                 "Setting preview callback failed");
983         }
984
985         private void UnregisterPreviewCallback()
986         {
987             CameraErrorFactory.ThrowIfError(Native.UnsetPreviewCallback(_handle),
988                 "Unsetting preview callback failed");
989             _previewCallback = null;
990         }
991
992         private void RegisterMediaPacketPreviewCallback()
993         {
994             _mediaPacketPreviewCallback = (IntPtr mediaPacket, IntPtr userData) =>
995             {
996                 MediaPacket packet = MediaPacket.From(mediaPacket);
997                 var eventHandler = _mediaPacketPreview;
998
999                 if (eventHandler != null)
1000                 {
1001                     eventHandler.Invoke(this, new MediaPacketPreviewEventArgs(packet));
1002                 }
1003                 else
1004                 {
1005                     packet.Dispose();
1006                 }
1007             };
1008             CameraErrorFactory.ThrowIfError(Native.SetMediaPacketPreviewCallback(_handle, _mediaPacketPreviewCallback, IntPtr.Zero),
1009                 "Setting media packet preview callback failed");
1010         }
1011
1012         private void UnregisterMediaPacketPreviewCallback()
1013         {
1014             CameraErrorFactory.ThrowIfError(Native.UnsetMediaPacketPreviewCallback(_handle),
1015                 "Unsetting media packet preview callback failed");
1016             _mediaPacketPreviewCallback = null;
1017         }
1018         #endregion Callback registrations
1019     }
1020 }