[ScreenMirroring] Fixed compile error.
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Remoting / ScreenMirroring / ScreenMirroring.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 using System;
17 using System.Diagnostics;
18 using System.Threading.Tasks;
19 using Native = Interop.ScreenMirroring;
20
21 namespace Tizen.Multimedia
22 {
23     static internal class ScreenMirroringLog
24     {
25         internal const string LogTag = "Tizen.Multimedia.ScreenMirroring";
26     }
27
28     /// <summary>
29     /// ScreenMirroring class provides methods to function as screen mirroring application as sink.
30     /// It gives the ability to connect to and disconnect from a screen mirroring source, and
31     /// start, pause, and resume the screen mirroring sink, set the resolution or display,
32     /// register state change callback function.
33     /// </summary>
34     public class ScreenMirroring : IDisposable, IDisplayable<int>
35     {
36         internal VideoInformation _videoInfo;
37         internal AudioInformation _audioInfo;
38         internal IntPtr _handle;
39         internal string _ip;
40         internal string _port;
41
42         private bool _disposed = false;
43         private EventHandler<StateChangedEventArgs> _stateChanged;
44         private Native.StateChangedCallback _stateChangedCallback;
45
46         /// <summary>
47         /// Initializes a new instance of the ScreenMirroring class with parameters Ip, Port and Display handle.
48         /// Object should be created only when Ip and Port are available.
49         /// Create(i.e constructor) api will create a new handle with the given parameters.
50         /// </summary>
51         /// <param name="display">Display.</param>
52         /// <param name="ip">Ip.</param>
53         /// <param name="port">Port.</param>
54         /// <exception cref="ArgumentException">Thrown when method fail due to an invalid parameter</exception>
55         public ScreenMirroring(Display display, string ip, string port)
56         {
57             int ret = Native.Create(out _handle);
58             if (ret != (int)ScreenMirroringError.None)
59             {
60                 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to create Screen Mirroring Sink");
61             }
62
63             // initiate values
64             _ip = ip;
65             _port = port;
66
67             // Set ip and port
68             int ret1 = Native.SetIpAndPort(_handle, _ip, _port);
69             if (ret1 != (int)ScreenMirroringError.None)
70             {
71                 Log.Error(ScreenMirroringLog.LogTag, "Set ip and port failed" + (ScreenMirroringError)ret1);
72                 ScreenMirroringErrorFactory.ThrowException(ret, "set ip and port failed");
73             }
74
75             Display = display;
76
77             // AudioInfo
78             _audioInfo = new AudioInformation();
79             _audioInfo._handle = _handle;
80             // VideoInfo
81             _videoInfo = new VideoInformation();
82             _videoInfo._handle = _handle;
83
84             Log.Debug(ScreenMirroringLog.LogTag, "screen mirroring sink created : " + _handle);
85         }
86
87         /// <summary>
88         /// Screen Mirroring destructor.
89         /// </summary>
90         ~ScreenMirroring()
91         {
92             Dispose(false);
93         }
94
95         /// <summary>
96         /// StateChanged event is raised when state change happens.
97         /// Must be called after Create() API.
98         /// </summary>
99         public event EventHandler<StateChangedEventArgs> StateChanged
100         {
101             add
102             {
103                 if (_stateChanged == null)
104                 {
105                     RegisterStateChangedEvent();
106                 }
107
108                 _stateChanged += value;
109             }
110
111             remove
112             {
113                 _stateChanged -= value;
114                 if (_stateChanged == null)
115                 {
116                     UnregisterStateChangedEvent();
117                 }
118             }
119         }
120
121         /// <summary>
122         /// Sets the server ip and port.
123         /// This must be called before connect() and after create().
124         /// </summary>
125         /// <example> If only one handle is used for toggling between more than two source devices,
126         /// then this API ahould be used to assign the parameters to the handle.
127         /// </example>
128         /// <param name="ip">Ip.</param>
129         /// <param name="port">Port.</param>
130         /// <exception cref="ArgumentException">Thrown when method fail due to an invalid parameter</exception>
131         public void SetIpAndPort(string ip, string port)
132         {
133             int ret = Native.SetIpAndPort(_handle, ip, port);
134             if (ret != (int)ScreenMirroringError.None)
135             {
136                 Log.Error(ScreenMirroringLog.LogTag, "Set ip and port failed" + (ScreenMirroringError)ret);
137                 ScreenMirroringErrorFactory.ThrowException(ret, "set ip and port failed");
138             }
139         }
140
141         /// <summary>
142         /// Set Resolution.
143         /// valid state: NULL..
144         /// </summary>
145         /// <param name="resolution"> example: (R1920x1080P30 | R1280x720P30) </param>
146         /// <exception cref="ArgumentException">Thrown when method fail due to an invalid parameter</exception>
147         public void SetResolution(ResolutionType resolution)
148         {
149             int ret = Native.SetResolution(_handle, (int)resolution);
150             if (ret != (int)ScreenMirroringError.None)
151             {
152                 Log.Error(ScreenMirroringLog.LogTag, "Set resolution failed" + (ScreenMirroringError)ret);
153                 ScreenMirroringErrorFactory.ThrowException(ret, "set resolution failed");
154             }
155         }
156
157         private Display _display;
158
159         private int ApplyDisplay(Display display)
160         {
161             return display.ApplyTo(this);
162         }
163
164         private void ReplaceDisplay(Display newDisplay)
165         {
166             _display?.SetOwner(null);
167             _display = newDisplay;
168             _display?.SetOwner(this);
169         }
170
171         /// <summary>
172         /// Sets the display.
173         /// This must be called before prepare() and after create().
174         /// </summary>
175         /// <example> If only one handle is used for toggling between more than two source devices,
176         /// then this API should be used to assign the parameters to the handle.
177         /// </example>
178         /// <exception cref="ArgumentException">Thrown when method fail due to an invalid parameter</exception>
179         public Display Display
180         {
181             get
182             {
183                 return _display;
184             }
185             set
186             {
187                 if (value == null)
188                 {
189                     throw new ArgumentNullException(nameof(Display));
190                 }
191
192                 int ret = ApplyDisplay(value);
193                 if (ret != (int)ScreenMirroringError.None)
194                 {
195                     Log.Error(ScreenMirroringLog.LogTag, "Set display failed" + (ScreenMirroringError)ret);
196                     ScreenMirroringErrorFactory.ThrowException(ret, "set display failed");
197                 }
198             }
199         }
200
201         int IDisplayable<int>.ApplyEvasDisplay(DisplayType type, ElmSharp.EvasObject evasObject)
202         {
203             Debug.Assert(_disposed == false);
204
205             Debug.Assert(Enum.IsDefined(typeof(DisplayType), type));
206
207             return Native.SetDisplay(_handle, (int)type, evasObject);
208         }
209
210         /// <summary>
211         /// Prepare this instance.
212         /// This must be called after Create().
213         /// </summary>
214         /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
215         public void Prepare()
216         {
217             int ret = Native.Prepare(_handle);
218             if (ret != (int)ScreenMirroringError.None)
219             {
220                 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to prepare sink for screen mirroring");
221             }
222         }
223
224         /// <summary>
225         /// Creates connection and prepare for receiving data from ScreenMirroring source.
226         /// This must be called after prepare().
227         /// </summary>
228         /// <remarks> It will not give the current state. Need to subscribe for event to get the current state </remarks>
229         /// <returns>bool value</returns>
230         /// <privilege>http://tizen.org/privilege/internet</privilege>
231         /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
232         public Task<bool> ConnectAsync()
233         {
234             int ret = Native.ConnectAsync(_handle);
235             var task = new TaskCompletionSource<bool>();
236
237             Task.Factory.StartNew(() =>
238                 {
239                     if (ret == (int)ScreenMirroringError.None)
240                     {
241                         task.SetResult(true);
242                     }
243
244                     else if (ret != (int)ScreenMirroringError.None)
245                     {
246                         Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
247                         InvalidOperationException e = new InvalidOperationException("Operation Failed");
248                         task.TrySetException(e);
249                     }
250                 });
251
252             return task.Task;
253         }
254
255         /// <summary>
256         /// Get AudioInfo.
257         /// This must be called after connectasync().
258         /// valid states: connected/playback/paused.
259         /// If audio file changes during playback again
260         /// then the current info should be retrieved from the audio information class.
261         /// </summary>
262         /// <value> AudioInfo object </value>
263         public AudioInformation AudioInfo
264         {
265             get
266             {
267                 return _audioInfo;
268             }
269         }
270
271         /// <summary>
272         /// Get VideoInfo.
273         /// This must be called after connectasync().
274         /// valid states: connected/playback/paused.
275         /// If video file changes during playback again
276         /// then the current info should be retrieved from the video information class.
277         /// </summary>
278         /// <value> VideoInfo object </value>
279         public VideoInformation VideoInfo
280         {
281             get
282             {
283                 return _videoInfo;
284             }
285         }
286
287         /// <summary>
288         /// Start receiving data from the ScreenMirroring source and display it(Mirror).
289         /// This must be called after connectasync().
290         ///  </summary>
291         /// <remarks> It will not give the current state. Need to subscribe for event to get the current state </remarks>
292         /// <returns>bool value<returns>
293         /// <privilege>http://tizen.org/privilege/internet</privilege>
294         /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
295         public Task<bool> StartAsync()
296         {
297             int ret = Native.StartAsync(_handle);
298             var task = new TaskCompletionSource<bool>();
299
300             Task.Factory.StartNew(() =>
301                 {
302                     if (ret == (int)ScreenMirroringError.None)
303                     {
304                         task.SetResult(true);
305                     }
306
307                     else if (ret != (int)ScreenMirroringError.None)
308                     {
309                         Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
310                         InvalidOperationException e = new InvalidOperationException("Operation Failed");
311                         task.TrySetException(e);
312                     }
313                 });
314
315             return task.Task;
316         }
317
318         /// <summary>
319         /// Pauses receiving data from the ScreenMirroring source.
320         /// This must be called after startasync().
321         /// </summary>
322         /// <remarks> It will not give the current state. Need to subscribe for event to get the current state </remarks>
323         /// <returns>bool value</returns>
324         /// <privilege>http://tizen.org/privilege/internet</privilege>
325         /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
326         public Task<bool> PauseAsync()
327         {
328             int ret = Native.PauseAsync(_handle);
329             var task = new TaskCompletionSource<bool>();
330
331             Task.Factory.StartNew(() =>
332                 {
333                     if (ret == (int)ScreenMirroringError.None)
334                     {
335                         task.SetResult(true);
336                     }
337
338                     else if (ret != (int)ScreenMirroringError.None)
339                     {
340                         Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
341                         InvalidOperationException e = new InvalidOperationException("Operation Failed");
342                         task.TrySetException(e);
343                     }
344                 });
345
346             return task.Task;
347         }
348
349         /// <summary>
350         /// Resumes receiving data from the ScreenMirroring source.
351         /// This must be called after pauseasync().
352         /// </summary>
353         /// <remarks> It will not give the current state. Need to subscribe for event to get the current state </remarks>
354         /// <returns>bool value</returns>
355         /// <privilege>http://tizen.org/privilege/internet</privilege>
356         /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
357         public Task<bool> ResumeAsync()
358         {
359             int ret = Native.ResumeAsync(_handle);
360             var task = new TaskCompletionSource<bool>();
361
362             Task.Factory.StartNew(() =>
363                 {
364                     if (ret == (int)ScreenMirroringError.None)
365                     {
366                         task.SetResult(true);
367                     }
368
369                     else if (ret != (int)ScreenMirroringError.None)
370                     {
371                         Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
372                         InvalidOperationException e = new InvalidOperationException("Operation Failed");
373                         task.TrySetException(e);
374                     }
375                 });
376
377             return task.Task;
378         }
379
380         /// <summary>
381         /// Disconnect this instance.
382         /// valid states: connected/playing/paused
383         /// </summary>
384         /// <privilege>http://tizen.org/privilege/internet</privilege>
385         /// <exception cref="ArgumentException">Thrown when method fail due to no connection between devices</exception>
386         public void Disconnect()
387         {
388             int ret = Native.Disconnect(_handle);
389             if (ret != (int)ScreenMirroringError.None)
390             {
391                 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to disconnect sink for screen mirroring");
392             }
393         }
394
395         /// <summary>
396         /// Unprepare this instance.
397         /// valid states: prepared/disconnected.
398         /// </summary>
399         /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
400         public void Unprepare()
401         {
402             int ret = Native.Unprepare(_handle);
403             if (ret != (int)ScreenMirroringError.None)
404             {
405                 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to reset the screen mirroring sink");
406             }
407         }
408
409         /// <summary>
410         /// Releases all resource used by the <see cref="Tizen.Multimedia.ScreenMirroring"/> object.
411         /// </summary>
412         /// <remarks>Call <see cref="Dispose"/> when you are finished using the <see cref="Tizen.Multimedia.ScreenMirroring"/>.
413         /// The <see cref="Dispose"/> method leaves the <see cref="Tizen.Multimedia.ScreenMirroring"/> in an unusable
414         /// state. After calling <see cref="Dispose"/>, you must release all references to the
415         /// <see cref="Tizen.Multimedia.ScreenMirroring"/> so the garbage collector can reclaim the memory that the
416         /// <see cref="Tizen.Multimedia.ScreenMirroring"/> was occupying.</remarks>
417         public void Dispose()
418         {
419             Dispose(true);
420             GC.SuppressFinalize(this);
421         }
422
423         /// <summary>
424         /// Dispose the specified handle.
425         /// </summary>
426         /// <param name="disposing">If set to <c>true</c> disposing.</param>
427         protected virtual void Dispose(bool disposing)
428         {
429             if (!_disposed)
430             {
431                 if (disposing)
432                 {
433                     // To be used if there are any other disposable objects
434                 }
435
436                 if (_handle != IntPtr.Zero)
437                 {
438                     Native.Destroy(_handle);
439                     _handle = IntPtr.Zero;
440                 }
441
442                 _disposed = true;
443             }
444         }
445
446         /// <summary>
447         /// Invoke the event for state or error.
448         /// </summary>
449         /// <param name="state"> state </param>
450         /// <param name="error"> error </param>
451         private void StateError(int state, int error)
452         {
453             ///if _stateChanged is subscribe, this will be invoke.
454             StateChangedEventArgs eventArgsState = new StateChangedEventArgs(state, error);
455             _stateChanged?.Invoke(this, eventArgsState);
456         }
457
458         /// <summary>
459         /// Registers the state changed event.
460         /// </summary>
461         /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
462         private void RegisterStateChangedEvent()
463         {
464             _stateChangedCallback = (IntPtr userData, int state, int error) =>
465                 {
466                     StateError(state, error);
467                 };
468
469             int ret = Native.SetStateChangedCb(_handle, _stateChangedCallback, IntPtr.Zero);
470             if (ret != (int)ScreenMirroringError.None)
471             {
472                 Log.Error(ScreenMirroringLog.LogTag, "Setting StateChanged callback failed" + (ScreenMirroringError)ret);
473                 ScreenMirroringErrorFactory.ThrowException(ret, "Setting StateChanged callback failed");
474             }
475         }
476
477         /// <summary>
478         /// Unregisters the state changed event.
479         /// </summary>
480         /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
481         private void UnregisterStateChangedEvent()
482         {
483             int ret = Native.UnsetStateChangedCb(_handle);
484             if (ret != (int)ScreenMirroringError.None)
485             {
486                 Log.Error(ScreenMirroringLog.LogTag, "Unsetting StateChnaged callback failed" + (ScreenMirroringError)ret);
487                 ScreenMirroringErrorFactory.ThrowException(ret, "Unsetting StateChanged callback failed");
488             }
489         }
490     }
491 }