f2d738128ecac4e3709a6e85f2892cc9b9630d90
[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.SetDisplay(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.SetDisplay(GetHandle(), type, evasObject);
461         }
462
463         CameraError IDisplayable<CameraError>.ApplyEcoreWindow(IntPtr windowHandle)
464         {
465             Debug.Assert(_disposed == false);
466
467             return CameraDisplay.SetEcoreDisplay(GetHandle(), windowHandle);
468         }
469
470         /// <summary>
471         /// Gets the state of the camera.
472         /// </summary>
473         /// <since_tizen> 3 </since_tizen>
474         /// <value> None, Created, Preview, Capturing, Captured.</value>
475         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
476         public CameraState State
477         {
478             get
479             {
480                 ValidateNotDisposed();
481
482                 CameraState val = CameraState.None;
483
484                 Native.GetState(_handle, out val).ThrowIfFailed("Failed to get camera state");
485
486                 return val;
487             }
488         }
489
490         /// <summary>
491         /// The hint for the display reuse.
492         /// If the hint is set to true, the display will be reused when the camera device is changed with
493         /// the ChangeDevice method.
494         /// </summary>
495         /// <since_tizen> 3 </since_tizen>
496         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
497         /// <exception cref="InvalidOperationException">An invalid state.</exception>
498         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
499         public bool DisplayReuseHint
500         {
501             get
502             {
503                 ValidateNotDisposed();
504
505                 Native.GetDisplayReuseHint(_handle, out bool val).ThrowIfFailed("Failed to get camera display reuse hint");
506
507                 return val;
508             }
509
510             set
511             {
512                 ValidateState(CameraState.Preview);
513
514                 Native.SetDisplayReuseHint(_handle, value).ThrowIfFailed("Failed to set display reuse hint.");
515             }
516         }
517
518         /// <summary>
519         /// Gets the facing direction of the camera module.
520         /// </summary>
521         /// <since_tizen> 3 </since_tizen>
522         /// <value>A <see cref="CameraFacingDirection"/> that specifies the facing direction of the camera device.</value>
523         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
524         public CameraFacingDirection Direction
525         {
526             get
527             {
528                 ValidateNotDisposed();
529
530                 Native.GetFacingDirection(_handle, out var val).ThrowIfFailed("Failed to get camera direction");
531
532                 return val;
533             }
534         }
535
536         /// <summary>
537         /// Gets the camera device count.
538         /// </summary>
539         /// <since_tizen> 3 </since_tizen>
540         /// <value>This returns 2, if the device supports primary and secondary cameras.
541         /// Otherwise 1, if the device only supports primary camera.</value>
542         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
543         public int CameraCount
544         {
545             get
546             {
547                 ValidateNotDisposed();
548
549                 Native.GetDeviceCount(_handle, out int val).ThrowIfFailed("Failed to get camera device count");
550
551                 return val;
552             }
553         }
554         #endregion Properties
555
556         #region Methods
557         /// <summary>
558         /// Changes the camera device.
559         /// </summary>
560         /// <param name="device">The hardware camera to access.</param>
561         /// <since_tizen> 3 </since_tizen>
562         /// <feature> http://tizen.org/feature/camera </feature>
563         /// <remarks>
564         /// If display reuse is set using <see cref="DisplayReuseHint"/>
565         /// before stopping the preview, the display will be reused and last frame on the display
566         /// can be kept even though camera device is changed.
567         /// The camera must be in the <see cref="CameraState.Created"/>.
568         /// </remarks>
569         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
570         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
571         /// <exception cref="NotSupportedException">In case of the ChangeDevice feature is not supported.</exception>
572         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
573         public void ChangeDevice(CameraDevice device)
574         {
575             ValidateState(CameraState.Created);
576             ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
577
578             Native.ChangeDevice(_handle, device).ThrowIfFailed("Failed to change the camera device");
579         }
580
581         /// <summary>
582         /// Gets the device state.
583         /// </summary>
584         /// <param name="device">The device to get the state.</param>
585         /// <returns>Returns the state of the camera device.</returns>
586         /// <since_tizen> 4 </since_tizen>
587         /// <feature> http://tizen.org/feature/camera </feature>
588         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
589         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
590         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
591         public static CameraDeviceState GetDeviceState(CameraDevice device)
592         {
593             ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
594
595             Native.GetDeviceState(device, out var val).ThrowIfFailed("Failed to get the camera device state.");
596
597             return val;
598         }
599
600         /// <summary>
601         /// Gets the flash state.
602         /// </summary>
603         /// <param name="device">The device to get the state.</param>
604         /// <returns>Returns the flash state of the camera device.</returns>
605         /// <since_tizen> 3 </since_tizen>
606         /// <feature> http://tizen.org/feature/camera </feature>
607         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
608         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
609         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
610         public static CameraFlashState GetFlashState(CameraDevice device)
611         {
612             ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
613
614             Native.GetFlashState(device, out var val).ThrowIfFailed("Failed to get camera flash state");
615
616             return val;
617         }
618
619         /// <summary>
620         /// Starts capturing and drawing preview frames on the screen.
621         /// The display property must be set using <see cref="Display"/> before using this method.
622         /// If needed set fps <see cref="CameraSettings.PreviewFps"/>, preview resolution
623         /// <see cref="CameraSettings.PreviewResolution"/>, or preview format <see cref="CameraSettings.PreviewPixelFormat"/>
624         /// before using this method.
625         /// The camera must be in the <see cref="CameraState.Created"/> or the <see cref="CameraState.Captured"/> state.
626         /// </summary>
627         /// <since_tizen> 3 </since_tizen>
628         /// <privilege> http://tizen.org/privilege/camera </privilege>
629         /// <feature> http://tizen.org/feature/camera </feature>
630         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
631         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
632         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
633         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
634         public void StartPreview()
635         {
636             ValidateState(CameraState.Created, CameraState.Captured);
637
638             Native.StartPreview(_handle).ThrowIfFailed("Failed to start the camera preview.");
639
640             // Update by StateChangedCallback can be delayed for dozens of milliseconds.
641             SetState(CameraState.Preview);
642         }
643
644         /// <summary>
645         /// Stops capturing and drawing preview frames on the screen.
646         /// The camera must be in the <see cref="CameraState.Preview"/> state.
647         /// </summary>
648         /// <since_tizen> 3 </since_tizen>
649         /// <privilege> http://tizen.org/privilege/camera </privilege>
650         /// <feature> http://tizen.org/feature/camera </feature>
651         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
652         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
653         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
654         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
655         public void StopPreview()
656         {
657             ValidateState(CameraState.Preview);
658
659             Native.StopPreview(_handle).ThrowIfFailed("Failed to stop the camera preview.");
660
661             SetState(CameraState.Created);
662         }
663
664         /// <summary>
665         /// Starts capturing of still images.
666         /// EventHandler must be set for capturing using <see cref="Capturing"/>
667         /// and for completed using <see cref="CaptureCompleted"/> before calling this method.
668         /// The camera must be in the <see cref="CameraState.Preview"/> state.
669         /// </summary>
670         /// <since_tizen> 3 </since_tizen>
671         /// <privilege> http://tizen.org/privilege/camera </privilege>
672         /// <feature> http://tizen.org/feature/camera </feature>
673         /// <remarks>
674         /// This function causes the transition of the camera state from capturing to captured
675         /// automatically and the corresponding EventHandlers will be invoked.
676         /// The preview should be restarted by calling the <see cref="StartPreview"/> method after capture is completed.
677         /// </remarks>
678         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
679         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
680         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
681         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
682         public void StartCapture()
683         {
684             ValidateState(CameraState.Preview);
685
686             Native.StartCapture(_handle, _capturingCallback, _captureCompletedCallback, IntPtr.Zero).
687                 ThrowIfFailed("Failed to start the camera capture.");
688
689             SetState(CameraState.Capturing);
690         }
691
692         /// <summary>
693         /// Starts continuously capturing still images.
694         /// EventHandler must be set for capturing using <see cref="Capturing"/>
695         /// and for completed using <see cref="CaptureCompleted"/> before calling this method.
696         /// The camera must be in the <see cref="CameraState.Preview"/> state.
697         /// </summary>
698         /// <param name="count">The number of still images.</param>
699         /// <param name="interval">The interval of the capture(milliseconds).</param>
700         /// <param name="cancellationToken">The cancellation token to cancel capturing.</param>
701         /// <seealso cref="CancellationToken"/>
702         /// <since_tizen> 3 </since_tizen>
703         /// <privilege> http://tizen.org/privilege/camera </privilege>
704         /// <feature> http://tizen.org/feature/camera </feature>
705         /// <remarks>
706         /// If this is not supported, zero shutter lag occurs. The capture resolution could be
707         /// changed to the preview resolution. This function causes the transition of the camera state
708         /// from capturing to captured automatically and the corresponding Eventhandlers will be invoked.
709         /// Each captured image will be delivered through Eventhandler set using the <see cref="Capturing"/> event.
710         /// The preview should be restarted by calling the <see cref="StartPreview"/> method after capture is completed.
711         /// </remarks>
712         /// <exception cref="ArgumentOutOfRangeException">In case of invalid parameters.</exception>
713         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
714         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
715         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
716         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
717         public void StartCapture(int count, int interval, CancellationToken cancellationToken)
718         {
719             ValidateState(CameraState.Preview);
720
721             if (count < 2)
722             {
723                 throw new ArgumentOutOfRangeException(nameof(count), count, $"{nameof(count)} should be greater than one.");
724             }
725
726             if (interval < 0)
727             {
728                 throw new ArgumentOutOfRangeException(nameof(interval), interval, $"{nameof(interval)} should be greater than or equal to zero.");
729             }
730
731             //Handle CancellationToken
732             if (cancellationToken != CancellationToken.None)
733             {
734                 cancellationToken.Register(() =>
735                 {
736                     Native.StopContinuousCapture(_handle).ThrowIfFailed("Failed to cancel the continuous capture");
737                     SetState(CameraState.Captured);
738                 });
739             }
740
741             Native.StartContinuousCapture(_handle, count, interval, _capturingCallback, _captureCompletedCallback, IntPtr.Zero).
742                 ThrowIfFailed("Failed to start the continuous capture.");
743
744             SetState(CameraState.Capturing);
745         }
746
747         /// <summary>
748         /// Starts camera auto-focusing, asynchronously.
749         /// The camera must be in the <see cref="CameraState.Preview"/> or the <see cref="CameraState.Captured"/> state.
750         /// </summary>
751         /// <param name="continuous">Continuous auto focus.</param>
752         /// <since_tizen> 3 </since_tizen>
753         /// <privilege> http://tizen.org/privilege/camera </privilege>
754         /// <feature> http://tizen.org/feature/camera </feature>
755         /// <remarks>
756         /// If continuous status is true, the camera continuously tries to focus.
757         /// </remarks>
758         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
759         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
760         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
761         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
762         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
763         public void StartFocusing(bool continuous)
764         {
765             ValidateState(CameraState.Preview, CameraState.Captured);
766
767             Native.StartFocusing(_handle, continuous).ThrowIfFailed("Failed to cancel the camera focus.");
768         }
769
770         /// <summary>
771         /// Stops camera auto focusing.
772         /// The camera must be in the <see cref="CameraState.Preview"/> or the <see cref="CameraState.Captured"/> state.
773         /// </summary>
774         /// <since_tizen> 3 </since_tizen>
775         /// <privilege> http://tizen.org/privilege/camera </privilege>
776         /// <feature> http://tizen.org/feature/camera </feature>
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 of.</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             Native.CancelFocusing(_handle).ThrowIfFailed("Failed to cancel the camera focus.");
786         }
787
788         /// <summary>
789         /// Starts face detection.
790         /// The camera must be in the <see cref="CameraState.Preview"/> state.
791         /// </summary>
792         /// <since_tizen> 3 </since_tizen>
793         /// <privilege> http://tizen.org/privilege/camera </privilege>
794         /// <feature> http://tizen.org/feature/camera </feature>
795         /// <remarks>
796         /// This should be called after <see cref="StartPreview"/> is started.
797         /// The Eventhandler set using <see cref="FaceDetected"/> is invoked when the face is detected in the preview frame.
798         /// Internally, it starts continuously focus and focusing on the detected face.
799         /// </remarks>
800         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
801         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
802         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
803         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
804         public void StartFaceDetection()
805         {
806             ValidateState(CameraState.Preview);
807
808             _faceDetectedCallback = (IntPtr faces, int count, IntPtr userData) =>
809             {
810                 var result = new List<FaceDetectionData>();
811                 IntPtr current = faces;
812
813                 for (int i = 0; i < count; i++)
814                 {
815                     result.Add(new FaceDetectionData(current));
816                     current = IntPtr.Add(current, Marshal.SizeOf<Native.DetectedFaceStruct>());
817                 }
818
819                 FaceDetected?.Invoke(this, new FaceDetectedEventArgs(result));
820             };
821
822             Native.StartFaceDetection(_handle, _faceDetectedCallback, IntPtr.Zero).
823                 ThrowIfFailed("Failed to start face detection");
824         }
825
826         /// <summary>
827         /// Stops face detection.
828         /// </summary>
829         /// <since_tizen> 3 </since_tizen>
830         /// <privilege> http://tizen.org/privilege/camera </privilege>
831         /// <feature> http://tizen.org/feature/camera </feature>
832         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
833         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
834         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
835         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
836         public void StopFaceDetection()
837         {
838             ValidateNotDisposed();
839
840             if (_faceDetectedCallback == null)
841             {
842                 throw new InvalidOperationException("The face detection is not started.");
843             }
844
845             Native.StopFaceDetection(_handle).ThrowIfFailed("Failed to stop the face detection.");
846
847             _faceDetectedCallback = null;
848         }
849         #endregion Methods
850
851         #region Callback registrations
852         private void RegisterCallbacks()
853         {
854             RegisterErrorCallback();
855             RegisterFocusStateChanged();
856             RegisterInterruptStartedCallback();
857             RegisterInterruptedCallback();
858             RegisterStateChangedCallback();
859
860             //Define capturing callback
861             _capturingCallback = (IntPtr main, IntPtr postview, IntPtr thumbnail, IntPtr userData) =>
862             {
863                 Capturing?.Invoke(this, new CameraCapturingEventArgs(new StillImage(main),
864                     postview == IntPtr.Zero ? null : new StillImage(postview),
865                     thumbnail == IntPtr.Zero ? null : new StillImage(thumbnail)));
866             };
867
868             //Define captureCompleted callback
869             _captureCompletedCallback = _ =>
870             {
871                 SetState(CameraState.Captured);
872                 CaptureCompleted?.Invoke(this, EventArgs.Empty);
873             };
874         }
875
876         private void RegisterInterruptStartedCallback()
877         {
878             _interruptStartedCallback = (CameraPolicy policy, CameraState state, IntPtr userData) =>
879             {
880                 InterruptStarted?.Invoke(this, new CameraInterruptStartedEventArgs(policy, state));
881             };
882
883             Native.SetInterruptStartedCallback(_handle, _interruptStartedCallback, IntPtr.Zero).
884                 ThrowIfFailed("Failed to set interrupt callback");
885         }
886
887         private void RegisterInterruptedCallback()
888         {
889             _interruptedCallback = (CameraPolicy policy, CameraState previous, CameraState current, IntPtr userData) =>
890             {
891                 Interrupted?.Invoke(this, new CameraInterruptedEventArgs(policy, previous, current));
892             };
893
894             Native.SetInterruptedCallback(_handle, _interruptedCallback, IntPtr.Zero).
895                 ThrowIfFailed("Failed to set interrupt callback");
896         }
897
898         private void RegisterErrorCallback()
899         {
900             _errorCallback = (CameraErrorCode error, CameraState current, IntPtr userData) =>
901             {
902                 ErrorOccurred?.Invoke(this, new CameraErrorOccurredEventArgs(error, current));
903             };
904
905             Native.SetErrorCallback(_handle, _errorCallback, IntPtr.Zero).ThrowIfFailed("Setting error callback failed");
906         }
907
908         private void RegisterStateChangedCallback()
909         {
910             _stateChangedCallback = (CameraState previous, CameraState current, bool byPolicy, IntPtr _) =>
911             {
912                 SetState(current);
913                 Log.Info(CameraLog.Tag, "Camera state changed " + previous.ToString() + " -> " + current.ToString());
914                 StateChanged?.Invoke(this, new CameraStateChangedEventArgs(previous, current, byPolicy));
915             };
916
917             Native.SetStateChangedCallback(_handle, _stateChangedCallback, IntPtr.Zero).
918                 ThrowIfFailed("Setting state changed callback failed");
919         }
920
921         private static void RegisterDeviceStateChangedCallback()
922         {
923             _deviceStateChangedCallback = (CameraDevice device, CameraDeviceState state, IntPtr userData) =>
924             {
925                 _deviceStateChanged?.Invoke(null, new CameraDeviceStateChangedEventArgs(device, state));
926             };
927
928             Native.SetDeviceStateChangedCallback(_deviceStateChangedCallback, IntPtr.Zero, out _deviceStateCallbackId).
929                 ThrowIfFailed("Failed to set device state changed callback");
930
931             Log.Info(CameraLog.Tag, "add callbackId " + _deviceStateCallbackId.ToString());
932         }
933
934         private static void UnregisterDeviceStateChangedCallback()
935         {
936             Native.UnsetDeviceStateChangedCallback(_deviceStateCallbackId).
937                 ThrowIfFailed("Unsetting device state changed callback failed");
938
939             _deviceStateChangedCallback = null;
940             _deviceStateCallbackId = 0;
941         }
942
943         private void RegisterFocusStateChanged()
944         {
945             _focusStateChangedCallback = (CameraFocusState state, IntPtr userData) =>
946             {
947                 FocusStateChanged?.Invoke(this, new CameraFocusStateChangedEventArgs(state));
948             };
949
950             Native.SetFocusStateChangedCallback(_handle, _focusStateChangedCallback, IntPtr.Zero).
951                 ThrowIfFailed("Setting focus changed callback failed");
952         }
953
954         private void RegisterHdrCaptureProgress()
955         {
956             _hdrCaptureProgressCallback = (int percent, IntPtr userData) =>
957             {
958                 _hdrCaptureProgress?.Invoke(this, new HdrCaptureProgressEventArgs(percent));
959             };
960
961             Native.SetHdrCaptureProgressCallback(_handle, _hdrCaptureProgressCallback, IntPtr.Zero).
962                 ThrowIfFailed("Setting Hdr capture progress callback failed");
963         }
964
965         private void UnregisterHdrCaptureProgress()
966         {
967             Native.UnsetHdrCaptureProgressCallback(_handle).
968                 ThrowIfFailed("Unsetting hdr capture progress is failed");
969
970             _hdrCaptureProgressCallback = null;
971         }
972
973         private void RegisterPreviewCallback()
974         {
975             _previewCallback = (IntPtr frame, IntPtr userData) =>
976             {
977                 _preview?.Invoke(this, new PreviewEventArgs(new PreviewFrame(frame)));
978             };
979
980             Native.SetPreviewCallback(_handle, _previewCallback, IntPtr.Zero).
981                 ThrowIfFailed("Setting preview callback failed");
982         }
983
984         private void UnregisterPreviewCallback()
985         {
986             Native.UnsetPreviewCallback(_handle).ThrowIfFailed("Unsetting preview callback failed");
987
988             _previewCallback = null;
989         }
990
991         private void RegisterMediaPacketPreviewCallback()
992         {
993             _mediaPacketPreviewCallback = (IntPtr mediaPacket, IntPtr userData) =>
994             {
995                 MediaPacket packet = MediaPacket.From(mediaPacket);
996
997                 var eventHandler = _mediaPacketPreview;
998
999                 if (eventHandler != null)
1000                 {
1001                     eventHandler.Invoke(this, new MediaPacketPreviewEventArgs(packet));
1002                 }
1003
1004                 packet.Dispose();
1005             };
1006
1007             Native.SetMediaPacketPreviewCallback(_handle, _mediaPacketPreviewCallback, IntPtr.Zero).
1008                 ThrowIfFailed("Setting media packet preview callback failed");
1009         }
1010
1011         private void UnregisterMediaPacketPreviewCallback()
1012         {
1013             Native.UnsetMediaPacketPreviewCallback(_handle).
1014                 ThrowIfFailed("Unsetting media packet preview callback failed");
1015
1016             _mediaPacketPreviewCallback = null;
1017         }
1018         #endregion Callback registrations
1019     }
1020 }