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