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