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