2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 using System.Diagnostics;
18 using System.Threading.Tasks;
19 using Native = Interop.ScreenMirroring;
21 namespace Tizen.Multimedia
23 static internal class ScreenMirroringLog
25 internal const string LogTag = "Tizen.Multimedia.ScreenMirroring";
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.
34 public class ScreenMirroring : IDisposable, IDisplayable<int>
36 internal VideoInformation _videoInfo;
37 internal AudioInformation _audioInfo;
38 internal IntPtr _handle;
40 internal string _port;
42 private bool _disposed = false;
43 private EventHandler<StateChangedEventArgs> _stateChanged;
44 private Native.StateChangedCallback _stateChangedCallback;
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.
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)
57 int ret = Native.Create(out _handle);
58 if (ret != (int)ScreenMirroringError.None)
60 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to create Screen Mirroring Sink");
68 int ret1 = Native.SetIpAndPort(_handle, _ip, _port);
69 if (ret1 != (int)ScreenMirroringError.None)
71 Log.Error(ScreenMirroringLog.LogTag, "Set ip and port failed" + (ScreenMirroringError)ret1);
72 ScreenMirroringErrorFactory.ThrowException(ret, "set ip and port failed");
78 _audioInfo = new AudioInformation();
79 _audioInfo._handle = _handle;
81 _videoInfo = new VideoInformation();
82 _videoInfo._handle = _handle;
84 Log.Debug(ScreenMirroringLog.LogTag, "screen mirroring sink created : " + _handle);
88 /// Screen Mirroring destructor.
96 /// StateChanged event is raised when state change happens.
97 /// Must be called after Create() API.
99 public event EventHandler<StateChangedEventArgs> StateChanged
103 if (_stateChanged == null)
105 RegisterStateChangedEvent();
108 _stateChanged += value;
113 _stateChanged -= value;
114 if (_stateChanged == null)
116 UnregisterStateChangedEvent();
122 /// Sets the server ip and port.
123 /// This must be called before connect() and after create().
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.
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)
133 int ret = Native.SetIpAndPort(_handle, ip, port);
134 if (ret != (int)ScreenMirroringError.None)
136 Log.Error(ScreenMirroringLog.LogTag, "Set ip and port failed" + (ScreenMirroringError)ret);
137 ScreenMirroringErrorFactory.ThrowException(ret, "set ip and port failed");
143 /// valid state: NULL..
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)
149 int ret = Native.SetResolution(_handle, (int)resolution);
150 if (ret != (int)ScreenMirroringError.None)
152 Log.Error(ScreenMirroringLog.LogTag, "Set resolution failed" + (ScreenMirroringError)ret);
153 ScreenMirroringErrorFactory.ThrowException(ret, "set resolution failed");
157 private Display _display;
159 private int ApplyDisplay(Display display)
161 return display.ApplyTo(this);
164 private void ReplaceDisplay(Display newDisplay)
166 _display?.SetOwner(null);
167 _display = newDisplay;
168 _display?.SetOwner(this);
172 /// Sets the display.
173 /// This must be called before prepare() and after create().
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.
178 /// <exception cref="ArgumentException">Thrown when method fail due to an invalid parameter</exception>
179 public Display Display
189 throw new ArgumentNullException(nameof(Display));
192 int ret = ApplyDisplay(value);
193 if (ret != (int)ScreenMirroringError.None)
195 Log.Error(ScreenMirroringLog.LogTag, "Set display failed" + (ScreenMirroringError)ret);
196 ScreenMirroringErrorFactory.ThrowException(ret, "set display failed");
201 int IDisplayable<int>.ApplyEvasDisplay(DisplayType type, ElmSharp.EvasObject evasObject)
203 Debug.Assert(_disposed == false);
205 Debug.Assert(Enum.IsDefined(typeof(DisplayType), type));
207 return Native.SetDisplay(_handle, (int)type, evasObject);
211 /// Prepare this instance.
212 /// This must be called after Create().
214 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
215 public void Prepare()
217 int ret = Native.Prepare(_handle);
218 if (ret != (int)ScreenMirroringError.None)
220 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to prepare sink for screen mirroring");
225 /// Creates connection and prepare for receiving data from ScreenMirroring source.
226 /// This must be called after prepare().
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()
234 int ret = Native.ConnectAsync(_handle);
235 var task = new TaskCompletionSource<bool>();
237 Task.Factory.StartNew(() =>
239 if (ret == (int)ScreenMirroringError.None)
241 task.SetResult(true);
244 else if (ret != (int)ScreenMirroringError.None)
246 Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
247 InvalidOperationException e = new InvalidOperationException("Operation Failed");
248 task.TrySetException(e);
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.
262 /// <value> AudioInfo object </value>
263 public AudioInformation AudioInfo
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.
278 /// <value> VideoInfo object </value>
279 public VideoInformation VideoInfo
288 /// Start receiving data from the ScreenMirroring source and display it(Mirror).
289 /// This must be called after connectasync().
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()
297 int ret = Native.StartAsync(_handle);
298 var task = new TaskCompletionSource<bool>();
300 Task.Factory.StartNew(() =>
302 if (ret == (int)ScreenMirroringError.None)
304 task.SetResult(true);
307 else if (ret != (int)ScreenMirroringError.None)
309 Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
310 InvalidOperationException e = new InvalidOperationException("Operation Failed");
311 task.TrySetException(e);
319 /// Pauses receiving data from the ScreenMirroring source.
320 /// This must be called after startasync().
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()
328 int ret = Native.PauseAsync(_handle);
329 var task = new TaskCompletionSource<bool>();
331 Task.Factory.StartNew(() =>
333 if (ret == (int)ScreenMirroringError.None)
335 task.SetResult(true);
338 else if (ret != (int)ScreenMirroringError.None)
340 Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
341 InvalidOperationException e = new InvalidOperationException("Operation Failed");
342 task.TrySetException(e);
350 /// Resumes receiving data from the ScreenMirroring source.
351 /// This must be called after pauseasync().
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()
359 int ret = Native.ResumeAsync(_handle);
360 var task = new TaskCompletionSource<bool>();
362 Task.Factory.StartNew(() =>
364 if (ret == (int)ScreenMirroringError.None)
366 task.SetResult(true);
369 else if (ret != (int)ScreenMirroringError.None)
371 Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
372 InvalidOperationException e = new InvalidOperationException("Operation Failed");
373 task.TrySetException(e);
381 /// Disconnect this instance.
382 /// valid states: connected/playing/paused
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()
388 int ret = Native.Disconnect(_handle);
389 if (ret != (int)ScreenMirroringError.None)
391 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to disconnect sink for screen mirroring");
396 /// Unprepare this instance.
397 /// valid states: prepared/disconnected.
399 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
400 public void Unprepare()
402 int ret = Native.Unprepare(_handle);
403 if (ret != (int)ScreenMirroringError.None)
405 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to reset the screen mirroring sink");
410 /// Releases all resource used by the <see cref="Tizen.Multimedia.ScreenMirroring"/> object.
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()
420 GC.SuppressFinalize(this);
424 /// Dispose the specified handle.
426 /// <param name="disposing">If set to <c>true</c> disposing.</param>
427 protected virtual void Dispose(bool disposing)
433 // To be used if there are any other disposable objects
436 if (_handle != IntPtr.Zero)
438 Native.Destroy(_handle);
439 _handle = IntPtr.Zero;
447 /// Invoke the event for state or error.
449 /// <param name="state"> state </param>
450 /// <param name="error"> error </param>
451 private void StateError(int state, int error)
453 ///if _stateChanged is subscribe, this will be invoke.
454 StateChangedEventArgs eventArgsState = new StateChangedEventArgs(state, error);
455 _stateChanged?.Invoke(this, eventArgsState);
459 /// Registers the state changed event.
461 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
462 private void RegisterStateChangedEvent()
464 _stateChangedCallback = (IntPtr userData, int state, int error) =>
466 StateError(state, error);
469 int ret = Native.SetStateChangedCb(_handle, _stateChangedCallback, IntPtr.Zero);
470 if (ret != (int)ScreenMirroringError.None)
472 Log.Error(ScreenMirroringLog.LogTag, "Setting StateChanged callback failed" + (ScreenMirroringError)ret);
473 ScreenMirroringErrorFactory.ThrowException(ret, "Setting StateChanged callback failed");
478 /// Unregisters the state changed event.
480 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
481 private void UnregisterStateChangedEvent()
483 int ret = Native.UnsetStateChangedCb(_handle);
484 if (ret != (int)ScreenMirroringError.None)
486 Log.Error(ScreenMirroringLog.LogTag, "Unsetting StateChnaged callback failed" + (ScreenMirroringError)ret);
487 ScreenMirroringErrorFactory.ThrowException(ret, "Unsetting StateChanged callback failed");