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 if (_display != null)
168 _display.Owner = null;
170 _display = newDisplay;
171 if (_display != null)
173 _display.Owner = this;
178 /// Sets the display.
179 /// This must be called before prepare() and after create().
181 /// <example> If only one handle is used for toggling between more than two source devices,
182 /// then this API should be used to assign the parameters to the handle.
184 /// <exception cref="ArgumentException">Thrown when method fail due to an invalid parameter</exception>
185 public Display Display
195 throw new ArgumentNullException(nameof(Display));
198 int ret = ApplyDisplay(value);
199 if (ret != (int)ScreenMirroringError.None)
201 Log.Error(ScreenMirroringLog.LogTag, "Set display failed" + (ScreenMirroringError)ret);
202 ScreenMirroringErrorFactory.ThrowException(ret, "set display failed");
207 int IDisplayable<int>.ApplyEvasDisplay(DisplayType type, ElmSharp.EvasObject evasObject)
209 Debug.Assert(_disposed == false);
211 Debug.Assert(Enum.IsDefined(typeof(DisplayType), type));
213 return Native.SetDisplay(_handle, (int)type, evasObject);
217 /// Prepare this instance.
218 /// This must be called after Create().
220 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
221 public void Prepare()
223 int ret = Native.Prepare(_handle);
224 if (ret != (int)ScreenMirroringError.None)
226 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to prepare sink for screen mirroring");
231 /// Creates connection and prepare for receiving data from ScreenMirroring source.
232 /// This must be called after prepare().
234 /// <remarks> It will not give the current state. Need to subscribe for event to get the current state </remarks>
235 /// <returns>bool value</returns>
236 /// <privilege>http://tizen.org/privilege/internet</privilege>
237 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
238 public Task<bool> ConnectAsync()
240 int ret = Native.ConnectAsync(_handle);
241 var task = new TaskCompletionSource<bool>();
243 Task.Factory.StartNew(() =>
245 if (ret == (int)ScreenMirroringError.None)
247 task.SetResult(true);
250 else if (ret != (int)ScreenMirroringError.None)
252 Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
253 InvalidOperationException e = new InvalidOperationException("Operation Failed");
254 task.TrySetException(e);
263 /// This must be called after connectasync().
264 /// valid states: connected/playback/paused.
265 /// If audio file changes during playback again
266 /// then the current info should be retrieved from the audio information class.
268 /// <value> AudioInfo object </value>
269 public AudioInformation AudioInfo
279 /// This must be called after connectasync().
280 /// valid states: connected/playback/paused.
281 /// If video file changes during playback again
282 /// then the current info should be retrieved from the video information class.
284 /// <value> VideoInfo object </value>
285 public VideoInformation VideoInfo
294 /// Start receiving data from the ScreenMirroring source and display it(Mirror).
295 /// This must be called after connectasync().
297 /// <remarks> It will not give the current state. Need to subscribe for event to get the current state </remarks>
298 /// <returns>bool value<returns>
299 /// <privilege>http://tizen.org/privilege/internet</privilege>
300 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
301 public Task<bool> StartAsync()
303 int ret = Native.StartAsync(_handle);
304 var task = new TaskCompletionSource<bool>();
306 Task.Factory.StartNew(() =>
308 if (ret == (int)ScreenMirroringError.None)
310 task.SetResult(true);
313 else if (ret != (int)ScreenMirroringError.None)
315 Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
316 InvalidOperationException e = new InvalidOperationException("Operation Failed");
317 task.TrySetException(e);
325 /// Pauses receiving data from the ScreenMirroring source.
326 /// This must be called after startasync().
328 /// <remarks> It will not give the current state. Need to subscribe for event to get the current state </remarks>
329 /// <returns>bool value</returns>
330 /// <privilege>http://tizen.org/privilege/internet</privilege>
331 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
332 public Task<bool> PauseAsync()
334 int ret = Native.PauseAsync(_handle);
335 var task = new TaskCompletionSource<bool>();
337 Task.Factory.StartNew(() =>
339 if (ret == (int)ScreenMirroringError.None)
341 task.SetResult(true);
344 else if (ret != (int)ScreenMirroringError.None)
346 Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
347 InvalidOperationException e = new InvalidOperationException("Operation Failed");
348 task.TrySetException(e);
356 /// Resumes receiving data from the ScreenMirroring source.
357 /// This must be called after pauseasync().
359 /// <remarks> It will not give the current state. Need to subscribe for event to get the current state </remarks>
360 /// <returns>bool value</returns>
361 /// <privilege>http://tizen.org/privilege/internet</privilege>
362 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
363 public Task<bool> ResumeAsync()
365 int ret = Native.ResumeAsync(_handle);
366 var task = new TaskCompletionSource<bool>();
368 Task.Factory.StartNew(() =>
370 if (ret == (int)ScreenMirroringError.None)
372 task.SetResult(true);
375 else if (ret != (int)ScreenMirroringError.None)
377 Log.Error(ScreenMirroringLog.LogTag, "Failed to start screen mirroring" + (ScreenMirroringError)ret);
378 InvalidOperationException e = new InvalidOperationException("Operation Failed");
379 task.TrySetException(e);
387 /// Disconnect this instance.
388 /// valid states: connected/playing/paused
390 /// <privilege>http://tizen.org/privilege/internet</privilege>
391 /// <exception cref="ArgumentException">Thrown when method fail due to no connection between devices</exception>
392 public void Disconnect()
394 int ret = Native.Disconnect(_handle);
395 if (ret != (int)ScreenMirroringError.None)
397 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to disconnect sink for screen mirroring");
402 /// Unprepare this instance.
403 /// valid states: prepared/disconnected.
405 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
406 public void Unprepare()
408 int ret = Native.Unprepare(_handle);
409 if (ret != (int)ScreenMirroringError.None)
411 ScreenMirroringErrorFactory.ThrowException(ret, "Failed to reset the screen mirroring sink");
416 /// Releases all resource used by the <see cref="Tizen.Multimedia.ScreenMirroring"/> object.
418 /// <remarks>Call <see cref="Dispose"/> when you are finished using the <see cref="Tizen.Multimedia.ScreenMirroring"/>.
419 /// The <see cref="Dispose"/> method leaves the <see cref="Tizen.Multimedia.ScreenMirroring"/> in an unusable
420 /// state. After calling <see cref="Dispose"/>, you must release all references to the
421 /// <see cref="Tizen.Multimedia.ScreenMirroring"/> so the garbage collector can reclaim the memory that the
422 /// <see cref="Tizen.Multimedia.ScreenMirroring"/> was occupying.</remarks>
423 public void Dispose()
426 GC.SuppressFinalize(this);
430 /// Dispose the specified handle.
432 /// <param name="disposing">If set to <c>true</c> disposing.</param>
433 protected virtual void Dispose(bool disposing)
439 // To be used if there are any other disposable objects
442 if (_handle != IntPtr.Zero)
444 Native.Destroy(_handle);
445 _handle = IntPtr.Zero;
453 /// Invoke the event for state or error.
455 /// <param name="state"> state </param>
456 /// <param name="error"> error </param>
457 private void StateError(int state, int error)
459 ///if _stateChanged is subscribe, this will be invoke.
460 StateChangedEventArgs eventArgsState = new StateChangedEventArgs(state, error);
461 _stateChanged?.Invoke(this, eventArgsState);
465 /// Registers the state changed event.
467 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
468 private void RegisterStateChangedEvent()
470 _stateChangedCallback = (IntPtr userData, int state, int error) =>
472 StateError(state, error);
475 int ret = Native.SetStateChangedCb(_handle, _stateChangedCallback, IntPtr.Zero);
476 if (ret != (int)ScreenMirroringError.None)
478 Log.Error(ScreenMirroringLog.LogTag, "Setting StateChanged callback failed" + (ScreenMirroringError)ret);
479 ScreenMirroringErrorFactory.ThrowException(ret, "Setting StateChanged callback failed");
484 /// Unregisters the state changed event.
486 /// <exception cref="InvalidOperationException">Thrown when method fail due to an internal error</exception>
487 private void UnregisterStateChangedEvent()
489 int ret = Native.UnsetStateChangedCb(_handle);
490 if (ret != (int)ScreenMirroringError.None)
492 Log.Error(ScreenMirroringLog.LogTag, "Unsetting StateChnaged callback failed" + (ScreenMirroringError)ret);
493 ScreenMirroringErrorFactory.ThrowException(ret, "Unsetting StateChanged callback failed");