[WebRTC] Add initial code (#2977)
authorHaesu Gwon <haesu.gwon@samsung.com>
Wed, 25 Aug 2021 00:54:54 +0000 (09:54 +0900)
committerGitHub <noreply@github.com>
Wed, 25 Aug 2021 00:54:54 +0000 (09:54 +0900)
* [Tizen.Multimedia.Remoting] Support WebRTC feature

37 files changed:
src/Tizen.Multimedia.Remoting/Interop/Interop.Libraries.cs [changed mode: 0644->0755]
src/Tizen.Multimedia.Remoting/Interop/Interop.WebRTC.DataChannel.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/Interop/Interop.WebRTC.SignalingServer.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/Interop/Interop.WebRTC.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaCameraSource.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaCustomSource.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaFileSource.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaMicrophoneSource.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaPacketBufferStatusChangedEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaPacketSource.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaPacketSourceConfiguration.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaScreenSource.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaSource.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaStreamTrack.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/MediaTestSource.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTC.Events.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTC.Properties.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTC.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannel.Events.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannel.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannelErrorOccurredEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannelEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannelMessageReceivedEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCEnums.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCError.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCErrorOccurredEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCFeatures.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCFrameEncodedEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCIceCandidateEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCIceConnectionStateChangedEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCIceGatheringStateChangedEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCPeerConnectionStateChangedEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCSignalingEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCSignalingServer.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCSignalingStateChangedEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCStateChangedEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia.Remoting/WebRTC/WebRTCTrackAddedEventArgs.cs [new file with mode: 0755]

old mode 100644 (file)
new mode 100755 (executable)
index 35c3f8d..1493ac7
@@ -20,5 +20,6 @@ internal static partial class Interop
     {
         public const string ScreenMirroring = "libcapi-media-screen-mirroring.so.0";
         public const string MediaController = "libcapi-media-controller.so.0";
+        public const string WebRTC = "libcapi-media-webrtc.so.0";
     }
 }
diff --git a/src/Tizen.Multimedia.Remoting/Interop/Interop.WebRTC.DataChannel.cs b/src/Tizen.Multimedia.Remoting/Interop/Interop.WebRTC.DataChannel.cs
new file mode 100755 (executable)
index 0000000..906d02d
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Runtime.InteropServices;
+using Tizen.Applications;
+using Tizen.Multimedia.Remoting;
+
+internal static partial class Interop
+{
+    internal static partial class NativeDataChannel
+    {
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void CreatedCallback(IntPtr handle, IntPtr dataChanndelHandle, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void OpenedCallback(IntPtr dataChannelHandle, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void MessageReceivedCallback(IntPtr dataChannelHandle, DataChannelType type, IntPtr message, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void ErrorOccurredCallback(IntPtr dataChanndelHandle, WebRTCErrorCode error, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void ClosedCallback(IntPtr dataChanndelHandle, IntPtr userData);
+
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_create_data_channel")]
+        internal static extern WebRTCErrorCode Create(IntPtr handle, string label, SafeBundleHandle bundle, out IntPtr dataChanndelHandle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_destroy_data_channel")]
+        internal static extern WebRTCErrorCode Destroy(IntPtr dataChanndelHandle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_send_string")]
+        internal static extern WebRTCErrorCode SendString(IntPtr dataChanndelHandle, string data);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_send_bytes")]
+        internal static extern WebRTCErrorCode SendBytes(IntPtr dataChanndelHandle, byte[] data, uint size);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_get_label")]
+        internal static extern WebRTCErrorCode GetLabel(IntPtr dataChanndelHandle, out string label);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_data")]
+        internal static extern WebRTCErrorCode GetData(IntPtr byteDataHandle, out IntPtr data, out ulong size);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_data_channel_cb")]
+        internal static extern WebRTCErrorCode SetCreatedByPeerCb(IntPtr handle, CreatedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_data_channel_cb")]
+        internal static extern WebRTCErrorCode UnsetCreatedByPeerCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_set_open_cb")]
+        internal static extern WebRTCErrorCode SetOpenedCb(IntPtr dataChanndelHandle, OpenedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_unset_open_cb")]
+        internal static extern WebRTCErrorCode UnsetOpenedCb(IntPtr dataChanndelHandle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_set_message_cb")]
+        internal static extern WebRTCErrorCode SetMessageReceivedCb(IntPtr dataChanndelHandle, MessageReceivedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_unset_message_cb")]
+        internal static extern WebRTCErrorCode UnsetMessageReceivedCb(IntPtr dataChanndelHandle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_set_error_cb")]
+        internal static extern WebRTCErrorCode SetErrorOccurredCb(IntPtr dataChanndelHandle, ErrorOccurredCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_unset_error_cb")]
+        internal static extern WebRTCErrorCode UnsetErrorOccurredCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_set_close_cb")]
+        internal static extern WebRTCErrorCode SetClosedCb(IntPtr dataChanndelHandle, ClosedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_data_channel_unset_close_cb")]
+        internal static extern WebRTCErrorCode UnsetClosedCb(IntPtr handle);
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia.Remoting/Interop/Interop.WebRTC.SignalingServer.cs b/src/Tizen.Multimedia.Remoting/Interop/Interop.WebRTC.SignalingServer.cs
new file mode 100755 (executable)
index 0000000..ade5eab
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Runtime.InteropServices;
+using Tizen.Multimedia.Remoting;
+
+internal static partial class Interop
+{
+    internal static class SignalingServer
+    {
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_signaling_server_create")]
+        internal static extern WebRTCErrorCode Create(int port, out IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_signaling_server_destroy")]
+        internal static extern WebRTCErrorCode Destroy(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_signaling_server_start")]
+        internal static extern WebRTCErrorCode Start(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_signaling_server_stop")]
+        internal static extern WebRTCErrorCode Stop(IntPtr handle);
+    }
+
+    internal static class SignalingClient
+    {
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_signaling_connect")]
+        internal static extern WebRTCErrorCode Connect(string serverIp, int port, SignalingMessageCallback callback, IntPtr userData, out IntPtr clientHandle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_signaling_disconnect")]
+        internal static extern WebRTCErrorCode Disconnect(IntPtr clientHandle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_signaling_request_session")]
+        internal static extern WebRTCErrorCode RequestSession(IntPtr clientHandle, int peerId);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_signaling_send_message")]
+        internal static extern WebRTCErrorCode SendMessage(IntPtr clientHandle, string message);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_signaling_get_id")]
+        internal static extern WebRTCErrorCode GetID(IntPtr clientHandle, out int id);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void SignalingMessageCallback(SignalingMessageType type, string message, IntPtr userData);
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia.Remoting/Interop/Interop.WebRTC.cs b/src/Tizen.Multimedia.Remoting/Interop/Interop.WebRTC.cs
new file mode 100755 (executable)
index 0000000..41606ba
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Runtime.InteropServices;
+using Tizen;
+using Tizen.Applications;
+using Tizen.Multimedia;
+using Tizen.Multimedia.Remoting;
+
+internal static partial class Interop
+{
+    internal static partial class NativeWebRTC
+    {
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void ErrorOccurredCallback(IntPtr handle, WebRTCErrorCode error, WebRTCState state, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void StateChangedCallback(IntPtr handle, WebRTCState previous, WebRTCState current, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void IceGatheringStateChangedCallback(IntPtr handle, WebRTCIceGatheringState state, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void SignalingStateChangedCallback(IntPtr handle, WebRTCSignalingState state, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void PeerConnectionStateChangedCallback(IntPtr handle, WebRTCPeerConnectionState state, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void IceConnectionStateChangedCallback(IntPtr handle, WebRTCIceConnectionState state, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void MediaPacketBufferStatusCallback(uint sourceId, MediaPacketBufferStatus status, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void NegotiationNeededCallback(IntPtr handle, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void IceCandidateCallback(IntPtr handle, string candidate, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void TrackAddedCallback(IntPtr handle, MediaType type, uint id, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void FrameEncodedCallback(IntPtr handle, MediaType type, uint trackId, IntPtr packetHandle, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate bool SupportedMediaFormatCallback(int format, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate bool RetrieveTurnServerCallback(string server, IntPtr userData);
+
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_create")]
+        internal static extern WebRTCErrorCode Create(out WebRTCHandle handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_destroy")]
+        internal static extern WebRTCErrorCode Destroy(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_start")]
+        internal static extern WebRTCErrorCode Start(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_stop")]
+        internal static extern WebRTCErrorCode Stop(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_state")]
+        internal static extern WebRTCErrorCode GetState(IntPtr handle, out WebRTCState state);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_ice_gathering_state")]
+        internal static extern WebRTCErrorCode GetIceGatheringState(IntPtr handle, out WebRTCIceGatheringState state);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_signaling_state")]
+        internal static extern WebRTCErrorCode GetSignalingState(IntPtr handle, out WebRTCSignalingState state);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_peer_connection_state")]
+        internal static extern WebRTCErrorCode GetPeerConnectionState(IntPtr handle, out WebRTCPeerConnectionState state);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_ice_connection_state")]
+        internal static extern WebRTCErrorCode GetIceConnectionState(IntPtr handle, out WebRTCIceConnectionState state);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_add_media_source")]
+        internal static extern WebRTCErrorCode AddMediaSource(IntPtr handle, MediaSourceType type, out uint sourceId);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_add_media_source_internal")]
+        internal static extern WebRTCErrorCode AddCustomMediaSource(IntPtr handle, CustomMediaSourceType type, out uint sourceId);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_remove_media_source")]
+        internal static extern WebRTCErrorCode RemoveMediaSource(IntPtr handle, uint sourceId);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_packet_source_set_format")]
+        internal static extern WebRTCErrorCode SetMediaPacketSourceInfo(IntPtr handle, uint sourceId, IntPtr packet);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_packet_source_push_packet")]
+        internal static extern WebRTCErrorCode PushMediaPacket(IntPtr handle, uint sourceId, IntPtr packet);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_file_source_set_path")]
+        internal static extern WebRTCErrorCode SetFileSourcePath(IntPtr handle, uint sourceId, string path);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_get_transceiver_direction")]
+        internal static extern WebRTCErrorCode GetTransceiverDirection(IntPtr handle, uint sourceId, MediaType type, out TransceiverDirection mode);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_set_transceiver_direction")]
+        internal static extern WebRTCErrorCode SetTransceiverDirection(IntPtr handle, uint sourceId, MediaType type, TransceiverDirection mode);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_set_pause")]
+        internal static extern WebRTCErrorCode SetPause(IntPtr handle, uint sourceId, MediaType type, bool pause);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_get_pause")]
+        internal static extern WebRTCErrorCode GetPause(IntPtr handle, uint sourceId, MediaType type, out bool isPaused);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_set_mute")]
+        internal static extern WebRTCErrorCode SetMute(IntPtr handle, uint sourceId, MediaType type, bool mute);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_get_mute")]
+        internal static extern WebRTCErrorCode GetMute(IntPtr handle, uint sourceId, MediaType type, out bool mute);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_set_video_resolution")]
+        internal static extern WebRTCErrorCode SetVideoResolution(IntPtr handle, uint sourceId, int width, int height);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_get_video_resolution")]
+        internal static extern WebRTCErrorCode GetVideoResolution(IntPtr handle, uint sourceId, out int width, out int height);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_set_audio_loopback")]
+        internal static extern WebRTCErrorCode SetAudioLoopback(IntPtr handle, uint sourceId, AudioStreamPolicyHandle streamInfo, out uint trackId);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_set_video_loopback")]
+        internal static extern WebRTCErrorCode SetVideoLoopback(IntPtr handle, uint sourceId, WebRTCDisplayType type, IntPtr display, out uint trackId);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_source_set_video_loopback_to_ecore_wl")]
+        internal static extern WebRTCErrorCode SetEcoreVideoLoopback(IntPtr handle, uint sourceId, IntPtr display, out uint trackId);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_mic_source_set_sound_stream_info")]
+        internal static extern WebRTCErrorCode SetAudioStreamPolicyToMicrophoneSource(IntPtr handle, uint sourceId, AudioStreamPolicyHandle streamInfo);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_sound_stream_info")]
+        internal static extern WebRTCErrorCode SetAudioStreamPolicy(IntPtr handle, uint trackId, AudioStreamPolicyHandle streamInfo);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_display")]
+        internal static extern WebRTCErrorCode SetDisplay(IntPtr handle, uint trackId, WebRTCDisplayType type, IntPtr display);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_ecore_wl_display")]
+        internal static extern WebRTCErrorCode SetEcoreDisplay(IntPtr handle, uint trackId, IntPtr display);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_display_mode")]
+        internal static extern WebRTCErrorCode SetDisplayMode(IntPtr handle, uint trackId, WebRTCDisplayMode mode);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_display_mode")]
+        internal static extern WebRTCErrorCode GetDisplayMode(IntPtr handle, uint trackId, out WebRTCDisplayMode mode);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_display_visible")]
+        internal static extern WebRTCErrorCode SetDisplayVisible(IntPtr handle, uint trackId, bool isVisible);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_display_visible")]
+        internal static extern WebRTCErrorCode GetDisplayVisible(IntPtr handle, uint trackId, out bool isVisible);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_stun_server")]
+        internal static extern WebRTCErrorCode GetStunServer(IntPtr handle, out string server);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_stun_server")]
+        internal static extern WebRTCErrorCode SetStunServer(IntPtr handle, string server);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_add_turn_server")]
+        internal static extern WebRTCErrorCode AddTurnServer(IntPtr handle, string server);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_foreach_turn_server")]
+        internal static extern WebRTCErrorCode ForeachTurnServer(IntPtr handle, RetrieveTurnServerCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_ice_transport_policy")]
+        internal static extern WebRTCErrorCode SetIceTransportPolicy(IntPtr handle, IceTransportPolicy policy);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_get_ice_transport_policy")]
+        internal static extern WebRTCErrorCode GetIceTransportPolicy(IntPtr handle, out IceTransportPolicy policy);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_create_offer")]
+        internal static extern WebRTCErrorCode CreateSDPOffer(IntPtr handle, SafeBundleHandle bundle, out string offer);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_create_answer")]
+        internal static extern WebRTCErrorCode CreateSDPAnswer(IntPtr handle, SafeBundleHandle bundle, out string offer);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_local_description")]
+        internal static extern WebRTCErrorCode SetLocalDescription(IntPtr handle, string description);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_remote_description")]
+        internal static extern WebRTCErrorCode SetRemoteDescription(IntPtr handle, string description);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_add_ice_candidate")]
+        internal static extern WebRTCErrorCode AddIceCandidate(IntPtr handle, string candidate);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_foreach_media_source_supported_format")]
+        internal static extern WebRTCErrorCode SupportedMediaSourceFormat(IntPtr handle, SupportedMediaFormatCallback callback, IntPtr userData);
+
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_error_cb")]
+        internal static extern WebRTCErrorCode SetErrorOccurredCb(IntPtr handle, ErrorOccurredCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_error_cb")]
+        internal static extern WebRTCErrorCode UnsetErrorOccurredCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_state_changed_cb")]
+        internal static extern WebRTCErrorCode SetStateChangedCb(IntPtr handle, StateChangedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_state_changed_cb")]
+        internal static extern WebRTCErrorCode UnsetStateChangedCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_ice_gathering_state_change_cb")]
+        internal static extern WebRTCErrorCode SetIceGatheringStateChangedCb(IntPtr handle, IceGatheringStateChangedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_ice_gathering_state_change_cb")]
+        internal static extern WebRTCErrorCode UnsetIceGatheringStateChangedCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_signaling_state_change_cb")]
+        internal static extern WebRTCErrorCode SetSignalingStateChangedCb(IntPtr handle, SignalingStateChangedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_signaling_state_change_cb")]
+        internal static extern WebRTCErrorCode UnsetSignalingStateChangedCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_peer_connection_state_change_cb")]
+        internal static extern WebRTCErrorCode SetPeerConnectionStateChangedCb(IntPtr handle, PeerConnectionStateChangedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_peer_connection_state_change_cb")]
+        internal static extern WebRTCErrorCode UnsetPeerConnectionStateChangedCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_ice_connection_state_change_cb")]
+        internal static extern WebRTCErrorCode SetIceConnectionStateChangedCb(IntPtr handle, IceConnectionStateChangedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_ice_connection_state_change_cb")]
+        internal static extern WebRTCErrorCode UnsetIceConnectionStateChangedCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_packet_source_set_buffer_state_changed_cb")]
+        internal static extern WebRTCErrorCode SetBufferStateChangedCb(IntPtr handle, uint sourceId, MediaPacketBufferStatusCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_media_packet_source_unset_buffer_state_changed_cb")]
+        internal static extern WebRTCErrorCode UnsetBufferStateChangedCb(IntPtr handle, uint sourceId);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_negotiation_needed_cb")]
+        internal static extern WebRTCErrorCode SetNegotiationNeededCb(IntPtr handle, NegotiationNeededCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_negotiation_needed_cb")]
+        internal static extern WebRTCErrorCode UnsetNegotiationNeededCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_ice_candidate_cb")]
+        internal static extern WebRTCErrorCode SetIceCandidateCb(IntPtr handle, IceCandidateCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_ice_candidate_cb")]
+        internal static extern WebRTCErrorCode UnsetIceCandidateCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_track_added_cb")]
+        internal static extern WebRTCErrorCode SetTrackAddedCb(IntPtr handle, TrackAddedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_track_added_cb")]
+        internal static extern WebRTCErrorCode UnsetTrackAddedCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_encoded_audio_frame_cb")]
+        internal static extern WebRTCErrorCode SetAudioFrameEncodedCb(IntPtr handle, FrameEncodedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_encoded_audio_frame_cb")]
+        internal static extern WebRTCErrorCode UnsetAudioFrameEncodedCb(IntPtr handle);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_set_encoded_video_frame_cb")]
+        internal static extern WebRTCErrorCode SetVideoFrameEncodedCb(IntPtr handle, FrameEncodedCallback callback, IntPtr userData = default);
+
+        [DllImport(Libraries.WebRTC, EntryPoint = "webrtc_unset_encoded_video_frame_cb")]
+        internal static extern WebRTCErrorCode UnsetVideoFrameEncodedCb(IntPtr handle);
+    }
+
+    internal class WebRTCHandle : SafeHandle
+    {
+        protected WebRTCHandle()
+          : base(IntPtr.Zero, true)
+        {
+        }
+
+        public override bool IsInvalid => handle == IntPtr.Zero;
+
+        protected override bool ReleaseHandle()
+        {
+            var ret = NativeWebRTC.Destroy(handle);
+            if (ret != WebRTCErrorCode.None)
+            {
+                return true;
+            }
+
+            Log.Debug(GetType().FullName, $"Failed to release native {GetType().Name}");
+            return false;
+        }
+    }
+
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaCameraSource.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaCameraSource.cs
new file mode 100755 (executable)
index 0000000..280fbc1
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Diagnostics;
+using NativeWebRTC = Interop.NativeWebRTC;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Represents a camera source.
+    /// </summary>
+    /// <remarks>The camera privilege(http://tizen.org/privilege/camera) is required.</remarks>
+    /// <seealso cref="WebRTC.AddSource"/>
+    /// <seealso cref="WebRTC.AddSources"/>
+    /// <since_tizen> 9 </since_tizen>
+    public sealed class MediaCameraSource : MediaSource
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MediaCameraSource"/> class.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaCameraSource() : base(MediaType.Video) {}
+
+        internal override void OnAttached(WebRTC webRtc)
+        {
+            Debug.Assert(webRtc != null);
+
+            if (WebRtc != null)
+            {
+                throw new InvalidOperationException("The source is has already been assigned to another WebRTC.");
+            }
+
+            NativeWebRTC.AddMediaSource(webRtc.Handle, MediaSourceType.Camera, out uint sourceId).
+                ThrowIfFailed("Failed to add MediaCameraSource.");
+
+            WebRtc = webRtc;
+            SourceId = sourceId;
+        }
+
+        internal override void OnDetached(WebRTC webRtc)
+        {
+            NativeWebRTC.RemoveMediaSource(webRtc.Handle, SourceId.Value).
+                ThrowIfFailed("Failed to remove MediaCameraSource.");
+
+            WebRtc = null;
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaCustomSource.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaCustomSource.cs
new file mode 100755 (executable)
index 0000000..4f42fd9
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using NativeWebRTC = Interop.NativeWebRTC;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Represents a audio, video custom source.
+    /// </summary>
+    /// <seealso cref="WebRTC.AddSource"/>
+    /// <seealso cref="WebRTC.AddSources"/>
+    /// <since_tizen> 9 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public sealed class MediaCustomSource : MediaSource
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MediaCustomSource"/> class.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public MediaCustomSource(MediaType type) : base(type) {}
+
+        internal override void OnAttached(WebRTC webRtc)
+        {
+            Debug.Assert(webRtc != null);
+
+            if (WebRtc != null)
+            {
+                throw new InvalidOperationException("The source is has already been assigned to another WebRTC.");
+            }
+
+            var type = MediaType == MediaType.Video ? CustomMediaSourceType.Video : CustomMediaSourceType.Audio;
+
+            NativeWebRTC.AddCustomMediaSource(webRtc.Handle, type, out uint sourceId).
+                    ThrowIfFailed($"Failed to add {MediaType.ToString()} MediaCustomSource.");
+
+            WebRtc = webRtc;
+            SourceId = sourceId;
+        }
+
+        internal override void OnDetached(WebRTC webRtc)
+        {
+            NativeWebRTC.RemoveMediaSource(webRtc.Handle, SourceId.Value).
+                ThrowIfFailed($"Failed to remove {MediaType.ToString()} MediaCustomSource.");
+
+            WebRtc = null;
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaFileSource.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaFileSource.cs
new file mode 100755 (executable)
index 0000000..42db970
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Diagnostics;
+using NativeWebRTC = Interop.NativeWebRTC;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Represents a file source.
+    /// </summary>
+    /// <remarks>
+    /// The media storage privilege(http://tizen.org/privilege/mediastorage) is required.<br/>
+    /// The external storage privilege(http://tizen.org/privilege/externalstorage) is required.
+    /// </remarks>
+    /// <seealso cref="WebRTC.AddSource"/>
+    /// <seealso cref="WebRTC.AddSources"/>
+    /// <since_tizen> 9 </since_tizen>
+    public sealed class MediaFileSource : MediaSource
+    {
+        private string _path;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MediaFileSource"/> class.
+        /// </summary>
+        /// <param name="type">The <see cref="MediaType"/> of file source.</param>
+        /// <param name="path">The file path.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaFileSource(MediaType type, string path) : base(type)
+        {
+            _path = path ?? throw new ArgumentNullException(nameof(path), "path is null");
+        }
+
+        internal override void OnAttached(WebRTC webRtc)
+        {
+            Debug.Assert(webRtc != null);
+
+            if (WebRtc != null)
+            {
+                throw new InvalidOperationException("The source is has already been assigned to another WebRTC.");
+            }
+
+            NativeWebRTC.AddMediaSource(webRtc.Handle, MediaSourceType.File, out uint sourceId).
+                ThrowIfFailed("Failed to add MediaFileSource.");
+
+            NativeWebRTC.SetFileSourcePath(webRtc.Handle, sourceId, _path).
+                ThrowIfFailed("Failed to set path for MediaFileSource.");
+
+            WebRtc = webRtc;
+            SourceId = sourceId;
+        }
+
+        internal override void OnDetached(WebRTC webRtc)
+        {
+            NativeWebRTC.RemoveMediaSource(webRtc.Handle, SourceId.Value).
+                ThrowIfFailed("Failed to remove MediaFileSource.");
+
+            WebRtc = null;
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaMicrophoneSource.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaMicrophoneSource.cs
new file mode 100755 (executable)
index 0000000..e5adf91
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Diagnostics;
+using NativeWebRTC = Interop.NativeWebRTC;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Represents a microphone source.
+    /// </summary>
+    /// <remarks>The recorder privilege(http://tizen.org/privilege/recorder) is required.</remarks>
+    /// <seealso cref="WebRTC.AddSource"/>
+    /// <seealso cref="WebRTC.AddSources"/>
+    /// <since_tizen> 9 </since_tizen>
+    public sealed class MediaMicrophoneSource : MediaSource
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MediaMicrophoneSource"/> class.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaMicrophoneSource() : base(MediaType.Audio) {}
+
+        internal override void OnAttached(WebRTC webRtc)
+        {
+            Debug.Assert(webRtc != null);
+            if (WebRtc != null)
+            {
+                Log.Error(WebRTCLog.Tag, "The source is has already been assigned to another WebRTC.");
+                throw new InvalidOperationException("The source is has already been assigned to another WebRTC.");
+            }
+
+            NativeWebRTC.AddMediaSource(webRtc.Handle, MediaSourceType.Microphone, out uint sourceId).
+                ThrowIfFailed("Failed to add MediaMicrophoneSource.");
+
+            WebRtc = webRtc;
+            SourceId = sourceId;
+        }
+
+        internal override void OnDetached(WebRTC webRtc)
+        {
+            NativeWebRTC.RemoveMediaSource(webRtc.Handle, SourceId.Value).
+                ThrowIfFailed("Failed to remove MediaMicrophoneSource.");
+
+            WebRtc = (WebRTC)null;
+        }
+
+        /// <summary>
+        /// Applies the audio stream policy to <see cref="MediaMicrophoneSource"/>.
+        /// </summary>
+        /// <param name="policy">The <see cref="AudioStreamPolicy"/> to apply.</param>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Idle"/> state.<br/>
+        /// <br/>
+        /// <see cref="WebRTC"/> does not support all <see cref="AudioStreamType"/>.<br/>
+        /// Supported types are <see cref="AudioStreamType.Media"/>, <see cref="AudioStreamType.VoiceRecognition"/>,
+        /// <see cref="AudioStreamType.Voip"/>, <see cref="AudioStreamType.MediaExternalOnly"/>.
+        /// </remarks>
+        /// <exception cref="ObjectDisposedException">
+        ///     The WebRTC has already been disposed.<br/>
+        ///     -or-<br/>
+        ///     <paramref name="policy"/> has already been disposed.
+        /// </exception>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="policy"/> is null.</exception>
+        /// <exception cref="NotSupportedException">
+        ///     <see cref="AudioStreamType"/> of <paramref name="policy"/> is not supported on the current platform.
+        /// </exception>
+        /// <seealso cref="AudioStreamPolicy"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void ApplyAudioStreamPolicy(AudioStreamPolicy policy)
+        {
+            if (policy == null)
+            {
+                throw new ArgumentNullException(nameof(policy), "policy is null");
+            }
+
+            WebRtc.ValidateWebRTCState(WebRTCState.Idle);
+
+            var ret = NativeWebRTC.SetAudioStreamPolicyToMicrophoneSource(WebRtc.Handle, SourceId.Value, policy.Handle);
+
+            if (ret == WebRTCErrorCode.InvalidArgument)
+            {
+                throw new NotSupportedException("The specified policy is not supported on the current system.");
+            }
+
+            ret.ThrowIfFailed("Failed to set the audio stream policy to the WebRTC");
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaPacketBufferStatusChangedEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaPacketBufferStatusChangedEventArgs.cs
new file mode 100755 (executable)
index 0000000..2f293e4
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="MediaPacketSourceConfiguration.BufferStatusChanged"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class MediaPacketBufferStatusChangedEventArgs : EventArgs
+    {
+        internal MediaPacketBufferStatusChangedEventArgs(uint sourceId, MediaPacketBufferStatus status)
+        {
+            SourceId = sourceId;
+            Status = status;
+        }
+
+        /// <summary>
+        /// Gets the source ID.
+        /// </summary>
+        /// <value>The source ID.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public uint SourceId { get; }
+
+        /// <summary>
+        /// Gets the media packet buffer status.
+        /// </summary>
+        /// <value>The media packet buffer status.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaPacketBufferStatus Status { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"Source ID={SourceId}, Buffer status={Status}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaPacketSource.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaPacketSource.cs
new file mode 100755 (executable)
index 0000000..99da490
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Represents a media packet source.
+    /// </summary>
+    /// <seealso cref="WebRTC.AddSource"/>
+    /// <seealso cref="WebRTC.AddSources"/>
+    /// <since_tizen> 9 </since_tizen>
+    public sealed class MediaPacketSource : MediaSource
+    {
+        private readonly MediaFormat _audioMediaFormat;
+        private readonly MediaFormat _videoMediaFormat;
+        private static List<MediaFormatAudioMimeType> _supportedAudioFormats;
+        private static List<MediaFormatVideoMimeType> _supportedVideoFormats;
+
+        /// <summary>
+        /// Gets all supported audio types.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public static IEnumerable<MediaFormatAudioMimeType> SupportedAudioTypes
+        {
+            get
+            {
+                GetSupportedTypes();
+                return _supportedAudioFormats.AsReadOnly();
+            }
+        }
+
+        /// <summary>
+        /// Gets all supported video types.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public static IEnumerable<MediaFormatVideoMimeType> SupportedVideoTypes
+        {
+            get
+            {
+                GetSupportedTypes();
+                return _supportedVideoFormats.AsReadOnly();
+            }
+        }
+
+        private static void GetSupportedTypes()
+        {
+            if (_supportedAudioFormats != null || _supportedVideoFormats != null)
+            {
+                return;
+            }
+
+            // Currentely, supported formats are fixed in native fw but I'll keep this for future use.
+            _supportedAudioFormats = new List<MediaFormatAudioMimeType>()
+            {
+                MediaFormatAudioMimeType.Vorbis,
+                MediaFormatAudioMimeType.Opus,
+                MediaFormatAudioMimeType.Pcm
+            };
+            _supportedVideoFormats = new List<MediaFormatVideoMimeType>()
+            {
+                MediaFormatVideoMimeType.H264SP,
+                MediaFormatVideoMimeType.H264MP,
+                MediaFormatVideoMimeType.H264HP,
+                MediaFormatVideoMimeType.MJpeg,
+                MediaFormatVideoMimeType.Vp8,
+                MediaFormatVideoMimeType.Vp9,
+                MediaFormatVideoMimeType.I420,
+                MediaFormatVideoMimeType.NV12
+            };
+        }
+
+        private MediaPacketSourceConfiguration CreateAudioConfiguration(AudioMediaFormat format)
+        {
+            if (format == null)
+            {
+                return null;
+            }
+
+            if (!SupportedAudioTypes.Contains<MediaFormatAudioMimeType>(format.MimeType))
+            {
+                throw new ArgumentException($"The audio format is not supported, Type : {format.MimeType}.");
+            }
+
+            return new MediaPacketSourceConfiguration(this);
+        }
+
+        private MediaPacketSourceConfiguration CreateVideoConfiguration(VideoMediaFormat format)
+        {
+            if (format == null)
+            {
+                return null;
+            }
+
+            if (!SupportedVideoTypes.Contains(format.MimeType))
+            {
+                throw new ArgumentException($"The video format is not supported, Type : {format.MimeType}.");
+            }
+            return new MediaPacketSourceConfiguration(this);
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the MediaPacketSource class with the specified <see cref="AudioMediaFormat"/>.
+        /// </summary>
+        /// <param name="audioMediaFormat">The <see cref="AudioMediaFormat"/> for this source.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="audioMediaFormat"/> is null.</exception>
+        /// <exception cref="ArgumentException"><paramref name="audioMediaFormat"/> is not supported.</exception>
+        /// <seealso cref="SupportedAudioTypes"/>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaPacketSource(AudioMediaFormat audioMediaFormat) : base(MediaType.Audio)
+        {
+            _audioMediaFormat = audioMediaFormat ?? throw new ArgumentNullException(nameof(audioMediaFormat));
+            AudioConfiguration = CreateAudioConfiguration(audioMediaFormat);
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the MediaPacketSource class with the specified <see cref="VideoMediaFormat"/>.
+        /// </summary>
+        /// <param name="videoMediaFormat">The <see cref="VideoMediaFormat"/> for this source.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="videoMediaFormat"/> is null.</exception>
+        /// <exception cref="ArgumentException"><paramref name="videoMediaFormat"/> is not supported.</exception>
+        /// <seealso cref="SupportedVideoTypes"/>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaPacketSource(VideoMediaFormat videoMediaFormat) : base(MediaType.Video)
+        {
+            _videoMediaFormat = videoMediaFormat ?? throw new ArgumentNullException(nameof(videoMediaFormat));
+            VideoConfiguration = CreateVideoConfiguration(videoMediaFormat);
+        }
+
+        /// <summary>
+        /// Gets the audio configuration, or null if no AudioMediaFormat is specified in the constructor.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaPacketSourceConfiguration AudioConfiguration { get; }
+
+        /// <summary>
+        /// Gets the video configuration, or null if no VideoMediaFormat is specified in the constructor.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaPacketSourceConfiguration VideoConfiguration { get; }
+
+        /// <summary>
+        /// Pushes elementary stream to decode audio or video.
+        /// </summary>
+        /// <remarks>
+        /// This source must be set as a source to a WebRTC and the WebRTC must be in the
+        /// <see cref="WebRTCState.Negotiating"/> or <see cref="WebRTCState.Playing"/> state
+        /// </remarks>
+        /// <param name="packet">The <see cref="MediaPacket"/> to decode.</param>
+        /// <exception cref="InvalidOperationException">
+        ///     This source is not set as a source to a WebRTC.<br/>
+        ///     -or-<br/>
+        ///     The WebRTC is not in the valid state.
+        /// </exception>
+        /// <exception cref="ArgumentNullException"><paramref name="packet"/> is null.</exception>
+        /// <exception cref="ObjectDisposedException"><paramref name="packet"/> has been disposed.</exception>
+        /// <exception cref="ArgumentException">
+        ///     <paramref name="packet"/> is neither video nor audio type.<br/>
+        ///     -or-<br/>
+        ///     The format of packet is not matched with the specified format in the constructor.
+        /// </exception>
+        /// <seealso cref="WebRTC.AddSource"/>
+        /// <seealso cref="WebRTC.AddSources"/>
+        /// <seealso cref="MediaPacket"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void Push(MediaPacket packet)
+        {
+            if (WebRtc == null)
+            {
+                Log.Error(WebRTCLog.Tag, "The source is not set as a source to a WebRTC yet.");
+                throw new InvalidOperationException("The source is not set as a source to a WebRTC yet.");
+            }
+
+            if (packet == null)
+            {
+                Log.Error(WebRTCLog.Tag, "packet is null");
+                throw new ArgumentNullException(nameof(packet));
+            }
+
+            if (packet.IsDisposed)
+            {
+                Log.Error(WebRTCLog.Tag, "packet is disposed");
+                throw new ObjectDisposedException(nameof(packet));
+            }
+
+            if (packet.Format.Type == MediaFormatType.Text || packet.Format.Type == MediaFormatType.Container)
+            {
+                Log.Error(WebRTCLog.Tag, "The format of the packet is invalid : " + packet.Format.Type);
+                throw new ArgumentException($"The format of the packet is invalid : {packet.Format.Type}.");
+            }
+
+            if (!packet.Format.Equals(_audioMediaFormat) && !packet.Format.Equals(_videoMediaFormat))
+            {
+                Log.Error(WebRTCLog.Tag, "The format of the packet is invalid : Unmatched format.");
+                throw new ArgumentException("The format of the packet is invalid : Unmatched format.");
+            }
+
+            if (packet.Format.Type == MediaFormatType.Video && _videoMediaFormat == null)
+            {
+                Log.Error(WebRTCLog.Tag, "Video is not configured with the current source.");
+                throw new ArgumentException("Video is not configured with the current source.");
+            }
+
+            if (packet.Format.Type == MediaFormatType.Audio && _audioMediaFormat == null)
+            {
+                Log.Error(WebRTCLog.Tag, "Audio is not configured with the current source.");
+                throw new ArgumentException("Audio is not configured with the current source.");
+            }
+
+            WebRtc.ValidateWebRTCState(WebRTCState.Negotiating, WebRTCState.Playing);
+
+            NativeWebRTC.PushMediaPacket(WebRtc.Handle, SourceId.Value, packet.GetHandle()).
+                ThrowIfFailed("Failed to push the packet to the WebRTC");
+        }
+
+        private void SetMediaStreamInfo(MediaFormat mediaFormat)
+        {
+            if (mediaFormat == null)
+            {
+                Log.Warn(WebRTCLog.Tag, "invalid media format");
+            }
+            else
+            {
+                IntPtr ptr = IntPtr.Zero;
+
+                try
+                {
+                    ptr = mediaFormat.AsNativeHandle();
+                    NativeWebRTC.SetMediaPacketSourceInfo(WebRtc.Handle, SourceId.Value, ptr).
+                        ThrowIfFailed("Failed to set the media stream info");
+                }
+                finally
+                {
+                    MediaFormat.ReleaseNativeHandle(ptr);
+                }
+            }
+        }
+
+        internal override void OnAttached(WebRTC webRtc)
+        {
+            Debug.Assert(webRtc != null);
+
+            if (WebRtc != null)
+            {
+                Log.Error(WebRTCLog.Tag, "The source is has already been assigned to another WebRTC.");
+                throw new InvalidOperationException("The source is has already been assigned to another WebRTC.");
+            }
+
+            NativeWebRTC.AddMediaSource(webRtc.Handle, MediaSourceType.MediaPacket, out uint sourceId).
+                ThrowIfFailed("Failed to add MediaPacketSource.");
+
+            WebRtc = webRtc;
+            SourceId = sourceId;
+
+            AudioConfiguration?.OnWebRTCSet();
+            VideoConfiguration?.OnWebRTCSet();
+
+            SetMediaStreamInfo(_audioMediaFormat);
+            SetMediaStreamInfo(_videoMediaFormat);
+        }
+
+        internal override void OnDetached(WebRTC webRtc)
+        {
+            NativeWebRTC.RemoveMediaSource(webRtc.Handle, SourceId.Value).
+                ThrowIfFailed("Failed to remove MediaPacketSource.");
+
+            AudioConfiguration?.OnWebRTCUnset();
+            VideoConfiguration?.OnWebRTCUnset();
+
+            WebRtc = null;
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaPacketSourceConfiguration.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaPacketSourceConfiguration.cs
new file mode 100755 (executable)
index 0000000..979e9ca
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides means to configure properties and handle events for <see cref="MediaPacketSource"/>.
+    /// </summary>
+    /// <seealso cref="MediaPacketSource"/>
+    /// <since_tizen> 9 </since_tizen>
+    public class MediaPacketSourceConfiguration
+    {
+        private readonly MediaPacketSource _owner;
+        NativeWebRTC.MediaPacketBufferStatusCallback _mediaPacketBufferStatusChangedCallback;
+
+        internal MediaPacketSourceConfiguration(MediaPacketSource owner)
+        {
+            _owner = owner;
+        }
+
+        /// <summary>
+        /// Occurs when the buffer underruns or overflows.
+        /// </summary>
+        /// <remarks>The event handler will be executed on an internal thread.</remarks>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<MediaPacketBufferStatusChangedEventArgs> BufferStatusChanged;
+
+        private IntPtr Handle => _owner.WebRtc.Handle;
+
+        private uint SourceId => _owner.SourceId.Value;
+
+        internal void OnWebRTCSet()
+        {
+            if (_mediaPacketBufferStatusChangedCallback == null)
+            {
+                RegisterBufferStatusChangedCallback();
+            }
+        }
+
+        internal void OnWebRTCUnset()
+        {
+            if (_mediaPacketBufferStatusChangedCallback != null)
+            {
+                UnregisterBufferStatusChangedCallback();
+            }
+        }
+
+        private void RegisterBufferStatusChangedCallback()
+        {
+            _mediaPacketBufferStatusChangedCallback = (sourceId_, state, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"sourceId:{sourceId_}, state:{state}");
+
+                BufferStatusChanged?.Invoke(this, new MediaPacketBufferStatusChangedEventArgs(sourceId_, state));
+            };
+
+            NativeWebRTC.SetBufferStateChangedCb(Handle, SourceId, _mediaPacketBufferStatusChangedCallback, IntPtr.Zero).
+                ThrowIfFailed("Failed to set buffer status changed callback.");
+        }
+
+        private void UnregisterBufferStatusChangedCallback()
+        {
+            NativeWebRTC.UnsetBufferStateChangedCb(Handle, SourceId).
+                ThrowIfFailed("Failed to unset buffer status changed callback.");
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaScreenSource.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaScreenSource.cs
new file mode 100755 (executable)
index 0000000..41d23fe
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Diagnostics;
+using NativeWebRTC = Interop.NativeWebRTC;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Represents a screen source.
+    /// </summary>
+    /// <seealso cref="WebRTC.AddSource"/>
+    /// <seealso cref="WebRTC.AddSources"/>
+    /// <since_tizen> 9 </since_tizen>
+    public sealed class MediaScreenSource : MediaSource
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MediaScreenSource"/> class.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaScreenSource() : base(MediaType.Video) {}
+
+        internal override void OnAttached(WebRTC webRtc)
+        {
+            Debug.Assert(webRtc != null);
+
+            if (WebRtc != null)
+            {
+                throw new InvalidOperationException("The source is has already been assigned to another WebRTC.");
+            }
+
+            NativeWebRTC.AddMediaSource(webRtc.Handle, MediaSourceType.Screen, out uint sourceId).
+                ThrowIfFailed("Failed to add MediaScreenSource.");
+
+            WebRtc = webRtc;
+            SourceId = sourceId;
+        }
+
+        internal override void OnDetached(WebRTC webRtc)
+        {
+            NativeWebRTC.RemoveMediaSource(webRtc.Handle, SourceId.Value).
+                ThrowIfFailed("Failed to remove MediaScreenSource.");
+
+            WebRtc = null;
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaSource.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaSource.cs
new file mode 100755 (executable)
index 0000000..1da2934
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using ElmSharp;
+using System;
+using System.Diagnostics;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// MediaSource is a base class for <see cref="WebRTC"/> sources.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public abstract class MediaSource : IDisplayable<uint>
+    {
+        internal WebRTC WebRtc { get; set; }
+        internal uint? SourceId { get; set; }
+        private Display _display;
+
+        /// <summary>
+        /// Gets the type of MediaSource.
+        /// </summary>
+        /// <value><see cref="MediaType"/></value>
+        /// <since_tizen> 9 </since_tizen>
+        protected MediaType MediaType { get; }
+
+        private bool IsDetached {get; set;} = false;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MediaSource"/> class.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        protected MediaSource(MediaType mediaType)
+        {
+            MediaType = mediaType;
+        }
+
+        internal void AttachTo(WebRTC webRtc)
+        {
+            if (IsDetached)
+            {
+                throw new InvalidOperationException("MediaSource was already detached.");
+            }
+
+            OnAttached(webRtc);
+        }
+
+        internal void DetachFrom(WebRTC webRtc)
+        {
+            OnDetached(webRtc);
+            IsDetached = true;
+        }
+
+        internal abstract void OnAttached(WebRTC webRtc);
+
+        internal abstract void OnDetached(WebRTC webRtc);
+
+        /// <summary>
+        /// Gets or sets the transceiver direction of current media source.
+        /// </summary>
+        /// <value>A <see cref="TransceiverDirection"/> that specifies the transceiver direction.</value>
+        /// <exception cref="InvalidOperationException">MediaSource is not attached yet.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public TransceiverDirection TransceiverDirection
+        {
+            get
+            {
+                if (!SourceId.HasValue)
+                {
+                    throw new InvalidOperationException("MediaSource is not attached yet. Call SetSource() first.");
+                }
+
+                NativeWebRTC.GetTransceiverDirection(WebRtc.Handle, SourceId.Value, MediaType, out TransceiverDirection mode).
+                    ThrowIfFailed("Failed to get transceiver direction.");
+
+                return mode;
+            }
+            set
+            {
+                if (!SourceId.HasValue)
+                {
+                    throw new InvalidOperationException("MediaSource is not attached yet. Call SetSource() first.");
+                }
+
+                NativeWebRTC.SetTransceiverDirection(WebRtc.Handle, SourceId.Value, MediaType, value).
+                    ThrowIfFailed("Failed to get transceiver direction.");
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the pause status of current media source.
+        /// </summary>
+        /// <value>A value that specifies the pause status.</value>
+        /// <exception cref="InvalidOperationException">MediaSource is not attached yet.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public bool Pause
+        {
+            get
+            {
+                NativeWebRTC.GetPause(WebRtc.Handle, SourceId.Value, MediaType, out bool isPaused).
+                    ThrowIfFailed("Failed to get pause");
+
+                return isPaused;
+            }
+            set
+            {
+                NativeWebRTC.SetPause(WebRtc.Handle, SourceId.Value, MediaType, value).
+                    ThrowIfFailed("Failed to set pause");
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the mute status of the current media source.
+        /// </summary>
+        /// <value>A value that specifies the mute status.</value>
+        /// <exception cref="InvalidOperationException">MediaSource is not attached yet.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public bool Mute
+        {
+            get
+            {
+                NativeWebRTC.GetMute(WebRtc.Handle, SourceId.Value, MediaType, out bool isMuted).
+                    ThrowIfFailed("Failed to get mute");
+
+                return isMuted;
+            }
+            set
+            {
+                NativeWebRTC.SetMute(WebRtc.Handle, SourceId.Value, MediaType, value).
+                    ThrowIfFailed("Failed to set mute");
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the video resolution of the current media source.
+        /// </summary>
+        /// <value>A value that specifies the mute status.</value>
+        /// <exception cref="ArgumentException">This source is not video source.</exception>
+        /// <exception cref="InvalidOperationException">MediaSource is not attached yet.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public Size VideoResolution
+        {
+            get
+            {
+                if (MediaType != MediaType.Video)
+                {
+                    throw new ArgumentException("This property is only for video.");
+                }
+
+                NativeWebRTC.GetVideoResolution(WebRtc.Handle, SourceId.Value, out int width, out int height).
+                    ThrowIfFailed("Failed to get video resolution");
+
+                return new Size(width, height);
+            }
+            set
+            {
+                if (MediaType != MediaType.Video)
+                {
+                    throw new ArgumentException("This property is only for video.");
+                }
+
+                NativeWebRTC.SetVideoResolution(WebRtc.Handle, SourceId.Value, value.Width, value.Height).
+                    ThrowIfFailed("Failed to set video resolution");
+            }
+        }
+
+        /// <summary>
+        /// Enables the audio loopback. The local audio will be played with <paramref name="policy"/>.
+        /// </summary>
+        /// <param name="policy">The <see cref="AudioStreamPolicy"/> to apply.</param>
+        /// <remarks>
+        /// <see cref="MediaSource"/> does not support all <see cref="AudioStreamType"/>.<br/>
+        /// Supported types are <see cref="AudioStreamType.Media"/>, <see cref="AudioStreamType.Voip"/>,
+        /// <see cref="AudioStreamType.MediaExternalOnly"/>.<br/>
+        /// </remarks>
+        /// <exception cref="ArgumentNullException"><paramref name="policy"/> is null.</exception>
+        /// <exception cref="InvalidOperationException">This MediaSource is not Audio</exception>
+        /// <exception cref="NotSupportedException">
+        ///     <see cref="AudioStreamType"/> of <paramref name="policy"/> is not supported on the current platform.
+        /// </exception>
+        /// <exception cref="ObjectDisposedException">
+        ///     <paramref name="policy"/> or WebRTC has already been disposed.
+        /// </exception>
+        /// <returns><see cref="MediaStreamTrack"/></returns>
+        public MediaStreamTrack EnableAudioLoopback(AudioStreamPolicy policy)
+        {
+            if (policy == null)
+            {
+                throw new ArgumentNullException(nameof(policy));
+            }
+
+            if (MediaType != MediaType.Audio)
+            {
+                throw new InvalidOperationException("AudioLoopback is only for Audio MediaSource");
+            }
+
+            var ret = NativeWebRTC.SetAudioLoopback(WebRtc.Handle, SourceId.Value, policy.Handle, out uint trackId);
+
+            if (ret == WebRTCErrorCode.InvalidArgument)
+            {
+                throw new NotSupportedException("The specified policy is not supported on the current system.");
+            }
+
+            ret.ThrowIfFailed("Failed to set the audio stream policy to the WebRTC");
+
+            return new MediaStreamTrack(WebRtc, MediaType, trackId);
+        }
+
+        private uint SetDisplay(Display display)
+            => display.ApplyTo(this);
+
+        private void ReplaceDisplay(Display newDisplay)
+        {
+            _display?.SetOwner(null);
+            _display = newDisplay;
+            _display?.SetOwner(this);
+        }
+
+        /// <summary>
+        /// Enables the video loopback. The local video will be diaplayed in <paramref name="display"/>.
+        /// </summary>
+        /// <param name="display">The <see cref="Display"/> to apply.</param>
+        /// <exception cref="ArgumentException">The display has already been assigned to another.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="display"/> is null.</exception>
+        /// <exception cref="InvalidOperationException">This MediaSource is not Video</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <returns><see cref="MediaStreamTrack"/></returns>
+        public MediaStreamTrack EnableVideoLoopback(Display display)
+        {
+            uint trackId = 0;
+
+            if (display == null)
+            {
+                throw new ArgumentNullException(nameof(display), "Display cannot be null.");
+            }
+
+            if (MediaType != MediaType.Video)
+            {
+                throw new InvalidOperationException("VideoLoopback is only for Video MediaSource");
+            }
+
+            if (display?.Owner != null)
+            {
+                if (ReferenceEquals(this, display.Owner))
+                {
+                    throw new ArgumentException("The display has already been assigned to another.");
+                }
+            }
+            else
+            {
+                trackId = SetDisplay(display);
+                ReplaceDisplay(display);
+            }
+
+            return new MediaStreamTrack(WebRtc, MediaType, trackId);
+        }
+
+        uint IDisplayable<uint>.ApplyEvasDisplay(DisplayType type, EvasObject evasObject)
+        {
+            Debug.Assert(Enum.IsDefined(typeof(DisplayType), type));
+            Debug.Assert(type != DisplayType.None);
+
+            NativeWebRTC.SetVideoLoopback(WebRtc.Handle, SourceId.Value,
+                type == DisplayType.Overlay ? WebRTCDisplayType.Overlay : WebRTCDisplayType.Evas, evasObject,
+                out uint trackId).ThrowIfFailed("Failed to set video loopback");
+
+            return trackId;
+        }
+
+        uint IDisplayable<uint>.ApplyEcoreWindow(IntPtr windowHandle)
+        {
+            NativeWebRTC.SetEcoreVideoLoopback(WebRtc.Handle, SourceId.Value, windowHandle, out uint trackId).
+                ThrowIfFailed("Failed to set ecore video loopback");
+
+            return trackId;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaStreamTrack.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaStreamTrack.cs
new file mode 100755 (executable)
index 0000000..688d752
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using ElmSharp;
+using System;
+using System.Diagnostics;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides the ability to control audio/video track.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public sealed class MediaStreamTrack : IDisplayable<WebRTCErrorCode>
+    {
+        private WebRTC _webRtc;
+        private uint _trackId;
+        private Display _display;
+
+        internal MediaStreamTrack(WebRTC webRtc, MediaType type, uint trackId)
+        {
+            _webRtc = webRtc;
+            _trackId = trackId;
+            Type = type;
+        }
+
+        /// <summary>
+        /// Gets the the of media stream track.
+        /// </summary>
+        /// <value><see cref="MediaType"/></value>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaType Type { get; }
+
+        private WebRTCErrorCode SetDisplay(Display display)
+            => display.ApplyTo(this);
+
+        private void ReplaceDisplay(Display newDisplay)
+        {
+            _display?.SetOwner(null);
+            _display = newDisplay;
+            _display?.SetOwner(this);
+        }
+
+        /// <summary>
+        /// Gets or sets the display to show remote video.
+        /// </summary>
+        /// <value>A <see cref="Multimedia.Display"/> that specifies the display.</value>
+        /// <remarks>
+        /// If user set video source with <see cref="TransceiverDirection.SendRecv"/>, <see cref="Display"/> must be set.<br/>
+        /// If not, the received video will fill entire screen.<br/>
+        /// If remote track, <see cref="Display"/> must be set in <see cref="WebRTC.TrackAdded"/> event.<br/>
+        /// The display is created with <see cref="MediaView"/>.
+        /// </remarks>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed of.</exception>
+        /// <exception cref="ArgumentException">The value has already been assigned to another WebRTC.</exception>
+        /// <exception cref="InvalidOperationException">The WebRTC is not called in <see cref="WebRTC.TrackAdded"/> event.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public Display Display
+        {
+            get => _display;
+            set
+            {
+                if (value == null)
+                {
+                    throw new ArgumentNullException(nameof(value), "Display cannot be null.");
+                }
+
+                if (value?.Owner != null)
+                {
+                    if (ReferenceEquals(this, value.Owner))
+                    {
+                        throw new ArgumentException("The display has already been assigned to another.");
+                    }
+                }
+                else
+                {
+                    SetDisplay(value).ThrowIfFailed("Failed to configure display of the MediaStreamTrack");
+                    ReplaceDisplay(value);
+                }
+            }
+        }
+
+        WebRTCErrorCode IDisplayable<WebRTCErrorCode>.ApplyEvasDisplay(DisplayType type, EvasObject evasObject)
+        {
+            Debug.Assert(Enum.IsDefined(typeof(DisplayType), type));
+            Debug.Assert(type != DisplayType.None);
+
+            return NativeWebRTC.SetDisplay(_webRtc.Handle, _trackId,
+                type == DisplayType.Overlay ? WebRTCDisplayType.Overlay : WebRTCDisplayType.Evas, evasObject);
+        }
+
+        WebRTCErrorCode IDisplayable<WebRTCErrorCode>.ApplyEcoreWindow(IntPtr windowHandle)
+        {
+            return NativeWebRTC.SetEcoreDisplay(_webRtc.Handle, _trackId, windowHandle);
+        }
+
+        /// <summary>
+        /// Gets or sets the display mode.
+        /// </summary>
+        /// <remarks>
+        /// This property is meaningful only in overlay or EVAS surface display type.
+        /// </remarks>
+        /// <value>A <see cref="WebRTCDisplayMode"/> that specifies the display mode.</value>
+        /// <exception cref="ArgumentException">Display mode type is incorrect.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCDisplayMode DisplayMode
+        {
+            get
+            {
+                NativeWebRTC.GetDisplayMode(_webRtc.Handle, _trackId, out var val).
+                    ThrowIfFailed("Failed to get WebRTC display mode");
+
+                return val;
+            }
+            set
+            {
+                ValidationUtil.ValidateEnum(typeof(WebRTCDisplayMode), value, nameof(value));
+
+                NativeWebRTC.SetDisplayMode(_webRtc.Handle, _trackId, value).
+                    ThrowIfFailed("Failed to set WebRTC display mode.");
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the display visibility.
+        /// </summary>
+        /// <value>true if WebRTC display is visible, otherwise false.</value>
+        /// <remarks>
+        /// This property is meaningful only in overlay or EVAS surface display type.
+        /// </remarks>
+        /// <since_tizen> 9 </since_tizen>
+        public bool DisplayVisible
+        {
+            get
+            {
+                NativeWebRTC.GetDisplayVisible(_webRtc.Handle, _trackId,out bool val).
+                    ThrowIfFailed("Failed to get visible status");
+
+                return val;
+            }
+            set
+            {
+                NativeWebRTC.SetDisplayVisible(_webRtc.Handle, _trackId, value).
+                    ThrowIfFailed("Failed to set display status.");
+            }
+        }
+
+        /// <summary>
+        /// Applies the audio stream policy to remote track.
+        /// </summary>
+        /// <param name="policy">The <see cref="AudioStreamPolicy"/> to apply.</param>
+        /// <remarks>
+        /// This must be called in <see cref="WebRTC.TrackAdded"/> event.<br/>
+        /// <br/>
+        /// <see cref="WebRTC"/> does not support all <see cref="AudioStreamType"/>.<br/>
+        /// Supported types are <see cref="AudioStreamType.Media"/>, <see cref="AudioStreamType.Voip"/>,
+        /// <see cref="AudioStreamType.MediaExternalOnly"/>.
+        /// </remarks>
+        /// <exception cref="ArgumentNullException"><paramref name="policy"/> is null.</exception>
+        /// <exception cref="InvalidOperationException">
+        /// <see cref="WebRTC.AudioFrameEncoded"/> was set.<br/>
+        /// -or-<br/>
+        /// This method was not called in <see cref="WebRTC.TrackAdded"/> event.
+        /// </exception>
+        /// <exception cref="NotSupportedException">
+        ///     <see cref="AudioStreamType"/> of <paramref name="policy"/> is not supported on the current platform.
+        /// </exception>
+        /// <exception cref="ObjectDisposedException">
+        ///     The WebRTC has already been disposed.<br/>
+        ///     -or-<br/>
+        ///     <paramref name="policy"/> has already been disposed.
+        /// </exception>
+        /// <seealso cref="AudioStreamPolicy"/>
+        /// <seealso cref="WebRTC.TrackAdded"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void ApplyAudioStreamPolicy(AudioStreamPolicy policy)
+        {
+            if (policy == null)
+            {
+                throw new ArgumentNullException(nameof(policy));
+            }
+
+            if (Type != MediaType.Audio)
+            {
+                throw new InvalidOperationException("Should be applied in Audio");
+            }
+
+            var ret = NativeWebRTC.SetAudioStreamPolicy(_webRtc.Handle, _trackId, policy.Handle);
+
+            if (ret == WebRTCErrorCode.InvalidArgument)
+            {
+                throw new NotSupportedException("The specified policy is not supported on the current system.");
+            }
+
+            ret.ThrowIfFailed("Failed to set the audio stream policy to the WebRTC");
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/MediaTestSource.cs b/src/Tizen.Multimedia.Remoting/WebRTC/MediaTestSource.cs
new file mode 100755 (executable)
index 0000000..84f38bd
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Diagnostics;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Represents an audio or a video test source.
+    /// </summary>
+    /// <seealso cref="WebRTC.AddSource"/>
+    /// <seealso cref="WebRTC.AddSources"/>
+    /// <since_tizen> 9 </since_tizen>
+    public sealed class MediaTestSource : MediaSource
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MediaTestSource"/> class.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaTestSource(MediaType type) : base(type) {}
+        internal override void OnAttached(WebRTC webRtc)
+        {
+            Debug.Assert(webRtc != null);
+
+            if (WebRtc != null)
+            {
+                throw new InvalidOperationException("The source is has already been assigned to another WebRTC.");
+            }
+
+            var type = MediaType == MediaType.Video ? MediaSourceType.VideoTest : MediaSourceType.AudioTest;
+
+            NativeWebRTC.AddMediaSource(webRtc.Handle, type, out uint sourceId).
+                ThrowIfFailed($"Failed to add {MediaType.ToString()} MediaTestSource.");
+
+            WebRtc = webRtc;
+            SourceId = sourceId;
+        }
+
+        internal override void OnDetached(WebRTC webRtc)
+        {
+            NativeWebRTC.RemoveMediaSource(webRtc.Handle, SourceId.Value).
+                ThrowIfFailed($"Failed to remove {MediaType.ToString()} MediaTestSource.");
+
+            WebRtc = null;
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTC.Events.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTC.Events.cs
new file mode 100755 (executable)
index 0000000..db4a80c
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides the ability to control WebRTC.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public partial class WebRTC
+    {
+        private NativeWebRTC.ErrorOccurredCallback _webRtcErrorOccurredCallback;
+        private NativeWebRTC.StateChangedCallback _webRtcStateChangedCallback;
+        private NativeWebRTC.IceGatheringStateChangedCallback _webRtcIceGatheringStateChangedCallback;
+        private NativeWebRTC.SignalingStateChangedCallback _webRtcSignalingStateChangedCallback;
+        private NativeWebRTC.PeerConnectionStateChangedCallback _webRtcPeerConnectionStateChangedCallback;
+        private NativeWebRTC.IceConnectionStateChangedCallback _webRtcIceConnectionStateChangedCallback;
+        private NativeWebRTC.NegotiationNeededCallback _webRtcNegotiationNeededCallback;
+        private NativeWebRTC.IceCandidateCallback _webRtcIceCandidateCallback;
+        private NativeWebRTC.TrackAddedCallback _webRtcTrackAddedCallback;
+        private NativeWebRTC.FrameEncodedCallback _webRtcAudioFrameEncodedCallback;
+        private NativeWebRTC.FrameEncodedCallback _webRtcVideoFrameEncodedCallback;
+        private NativeDataChannel.CreatedCallback _webRtcDataChannelCreatedCallback;
+
+        /// <summary>
+        /// Occurs when any error occurs.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCErrorOccurredEventArgs> ErrorOccurred;
+
+        /// <summary>
+        /// Occurs when WebRTC state is changed.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCStateChangedEventArgs> StateChanged;
+
+        /// <summary>
+        /// Occurs when the WebRTC ICE gathering state is changed.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCIceGatheringStateChangedEventArgs> IceGatheringStateChanged;
+
+        /// <summary>
+        /// Occurs when the WebRTC signaling state is changed.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCSignalingStateChangedEventArgs> SignalingStateChanged;
+
+        /// <summary>
+        /// Occurs when the WebRTC peer connection state is changed.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCPeerConnectionStateChangedEventArgs> PeerConnectionStateChanged;
+
+        /// <summary>
+        /// Occurs when the WebRTC ICE connection state is changed.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCIceConnectionStateChangedEventArgs> IceConnectionStateChanged;
+
+        /// <summary>
+        /// Occurs when negotiation is needed.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<EventArgs> NegotiationNeeded;
+
+        /// <summary>
+        /// Occurs when the WebRTC needs to send the ICE candidate message to the remote peer.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCIceCandidateEventArgs> IceCandidate;
+
+        /// <summary>
+        /// Occurs when a new track has been added to the WebRTC.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCTrackAddedEventArgs> TrackAdded;
+
+        private event EventHandler<WebRTCFrameEncodedEventArgs> _audioFrameEncoded;
+
+        /// <summary>
+        /// Occurs when each audio frame is ready to render.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCFrameEncodedEventArgs> AudioFrameEncoded
+        {
+            add
+            {
+                if (_audioFrameEncoded == null)
+                {
+                    RegisterAudioFrameEncodedCallback();
+                }
+
+                _audioFrameEncoded += value;
+            }
+            remove
+            {
+                _audioFrameEncoded -= value;
+
+                if (_audioFrameEncoded == null)
+                {
+                    UnregisterAudioFrameEncodedCallback();
+                }
+            }
+        }
+
+        private event EventHandler<WebRTCFrameEncodedEventArgs> _videoFrameEncoded;
+
+        /// <summary>
+        /// Occurs when each video frame is ready to render.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCFrameEncodedEventArgs> VideoFrameEncoded
+        {
+            add
+            {
+                if (_videoFrameEncoded == null)
+                {
+                    RegisterVideoFrameEncodedCallback();
+                }
+
+                _videoFrameEncoded += value;
+            }
+            remove
+            {
+                _videoFrameEncoded -= value;
+
+                if (_videoFrameEncoded == null)
+                {
+                    UnregisterVideoFrameEncodedCallback();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Occurs when the data channel is created to the connection by the remote peer.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCDataChannelEventArgs> DataChannel;
+
+        private void RegisterEvents()
+        {
+            RegisterErrorOccurredCallback();
+            RegisterStateChangedCallback();
+            RegisterIceGatheringStateChangedCallback();
+            RegisterSignalingStateChangedCallback();
+            RegisterPeerConnectionStateChangedCallback();
+            RegisterIceConnectionStateChangedCallback();
+            RegisterNegotiationNeededCallback();
+            RegisterIceCandidateCallback();
+            RegisterTrackAddedCallback();
+            RegisterDataChannelCreatedCallback();
+        }
+
+        private void RegisterErrorOccurredCallback()
+        {
+            _webRtcErrorOccurredCallback = (handle, error, state, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"{error}, {state}");
+
+                ErrorOccurred?.Invoke(this, new WebRTCErrorOccurredEventArgs((WebRTCError)error, state));
+            };
+
+            NativeWebRTC.SetErrorOccurredCb(Handle, _webRtcErrorOccurredCallback).
+                ThrowIfFailed("Failed to set error occurred callback.");
+        }
+
+        private void RegisterStateChangedCallback()
+        {
+            _webRtcStateChangedCallback = (handle, previous, current, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"{previous}, {current}");
+
+                StateChanged?.Invoke(this, new WebRTCStateChangedEventArgs(previous, current));
+            };
+
+            NativeWebRTC.SetStateChangedCb(Handle, _webRtcStateChangedCallback).
+                ThrowIfFailed("Failed to set state changed callback.");
+        }
+
+        private void RegisterIceGatheringStateChangedCallback()
+        {
+            _webRtcIceGatheringStateChangedCallback = (handle, state, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"Ice gathering state : {state}");
+
+                IceGatheringStateChanged?.Invoke(this, new WebRTCIceGatheringStateChangedEventArgs(state));
+            };
+
+            NativeWebRTC.SetIceGatheringStateChangedCb(Handle, _webRtcIceGatheringStateChangedCallback).
+                ThrowIfFailed("Failed to set Ice gathering state changed callback.");
+        }
+
+        private void RegisterSignalingStateChangedCallback()
+        {
+            _webRtcSignalingStateChangedCallback = (handle, state, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"Signaling state : {state}");
+
+                SignalingStateChanged?.Invoke(this, new WebRTCSignalingStateChangedEventArgs(state));
+            };
+
+            NativeWebRTC.SetSignalingStateChangedCb(Handle, _webRtcSignalingStateChangedCallback).
+                ThrowIfFailed("Failed to set signaling state changed callback.");
+        }
+
+        private void RegisterPeerConnectionStateChangedCallback()
+        {
+            _webRtcPeerConnectionStateChangedCallback = (handle, state, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"Peer connection state : {state}");
+
+                PeerConnectionStateChanged?.Invoke(this, new WebRTCPeerConnectionStateChangedEventArgs(state));
+            };
+
+            NativeWebRTC.SetPeerConnectionStateChangedCb(Handle, _webRtcPeerConnectionStateChangedCallback).
+                ThrowIfFailed("Failed to set peer connection state changed callback.");
+        }
+
+        private void RegisterIceConnectionStateChangedCallback()
+        {
+            _webRtcIceConnectionStateChangedCallback = (handle, state, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"Ice connection state : {state}");
+
+                IceConnectionStateChanged?.Invoke(this, new WebRTCIceConnectionStateChangedEventArgs(state));
+            };
+
+            NativeWebRTC.SetIceConnectionStateChangedCb(Handle, _webRtcIceConnectionStateChangedCallback).
+                ThrowIfFailed("Failed to set ICE connection state changed callback.");
+        }
+
+        private void RegisterNegotiationNeededCallback()
+        {
+            _webRtcNegotiationNeededCallback = (handle, _) =>
+            {
+                NegotiationNeeded?.Invoke(this, new EventArgs());
+            };
+
+            NativeWebRTC.SetNegotiationNeededCb(Handle, _webRtcNegotiationNeededCallback).
+                ThrowIfFailed("Failed to set negotiation needed callback.");
+        }
+
+        private void RegisterIceCandidateCallback()
+        {
+            _webRtcIceCandidateCallback = (handle, candidate, _) =>
+            {
+                IceCandidate?.Invoke(this, new WebRTCIceCandidateEventArgs(candidate));
+            };
+
+            NativeWebRTC.SetIceCandidateCb(Handle, _webRtcIceCandidateCallback).
+                ThrowIfFailed("Failed to set ice candidate callback.");
+        }
+
+        private void RegisterTrackAddedCallback()
+        {
+            _webRtcTrackAddedCallback = (handle, type, id, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"Track type[{type}], id[{id}]");
+
+                TrackAdded?.Invoke(this, new WebRTCTrackAddedEventArgs(new MediaStreamTrack(this, type, id)));
+            };
+
+            NativeWebRTC.SetTrackAddedCb(Handle, _webRtcTrackAddedCallback).
+                ThrowIfFailed("Failed to set track added callback.");
+        }
+
+        private void RegisterAudioFrameEncodedCallback()
+        {
+            _webRtcAudioFrameEncodedCallback = (handle, type, id, packet, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"Track type[{type}], id[{id}]");
+
+                _audioFrameEncoded?.Invoke(this,
+                    new WebRTCFrameEncodedEventArgs(new MediaStreamTrack(this, type, id), MediaPacket.From(packet)));
+            };
+
+            NativeWebRTC.SetAudioFrameEncodedCb(Handle, _webRtcAudioFrameEncodedCallback).
+                ThrowIfFailed("Failed to set audio frame encoded callback.");
+        }
+
+        private void UnregisterAudioFrameEncodedCallback()
+        {
+            NativeWebRTC.UnsetAudioFrameEncodedCb(Handle).
+                ThrowIfFailed("Failed to unset audio frame encoded callback.");
+        }
+
+        private void RegisterVideoFrameEncodedCallback()
+        {
+            _webRtcVideoFrameEncodedCallback = (handle, type, id, packet, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"Track type[{type}], id[{id}]");
+
+                _videoFrameEncoded?.Invoke(this,
+                    new WebRTCFrameEncodedEventArgs(new MediaStreamTrack(this, type, id), MediaPacket.From(packet)));
+            };
+
+            NativeWebRTC.SetVideoFrameEncodedCb(Handle, _webRtcVideoFrameEncodedCallback).
+                ThrowIfFailed("Failed to set video frame encoded callback.");
+        }
+
+        private void UnregisterVideoFrameEncodedCallback()
+        {
+            NativeWebRTC.UnsetVideoFrameEncodedCb(Handle).
+                ThrowIfFailed("Failed to unset video frame encoded callback.");
+        }
+
+        private void RegisterDataChannelCreatedCallback()
+        {
+            _webRtcDataChannelCreatedCallback = (handle, dataChannelHandle, _) =>
+            {
+                Log.Debug(WebRTCLog.Tag, "Invoked");
+
+                DataChannel?.Invoke(this, new WebRTCDataChannelEventArgs(dataChannelHandle));
+            };
+
+            NativeDataChannel.SetCreatedByPeerCb(Handle, _webRtcDataChannelCreatedCallback).
+                ThrowIfFailed("Failed to set data channel created callback.");
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTC.Properties.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTC.Properties.cs
new file mode 100755 (executable)
index 0000000..5e7c0ce
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using ElmSharp;
+using System;
+using System.Diagnostics;
+using static Interop;
+using NativeWebRTC = Interop.NativeWebRTC;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides the ability to control WebRTC.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public partial class WebRTC
+    {
+        internal IntPtr Handle
+        {
+            get
+            {
+                ValidateNotDisposed();
+                return _handle.DangerousGetHandle();
+            }
+        }
+
+        /// <summary>
+        /// Gets the state of the WebRTC.
+        /// </summary>
+        /// <value>The current state of the WebRTC.</value>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCState State
+        {
+            get
+            {
+                ValidateNotDisposed();
+
+                NativeWebRTC.GetState(Handle, out WebRTCState state).
+                    ThrowIfFailed("Failed to retrieve the state of the WebRTC");
+
+                Debug.Assert(Enum.IsDefined(typeof(WebRTCState), state));
+
+                return state;
+            }
+        }
+
+        /// <summary>
+        /// Gets the Ice gathering state of the WebRTC.
+        /// </summary>
+        /// <value>The current Ice gathering state of the WebRTC.</value>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCIceGatheringState IceGatheringState
+        {
+            get
+            {
+                ValidateNotDisposed();
+
+                NativeWebRTC.GetIceGatheringState(Handle, out WebRTCIceGatheringState state).
+                    ThrowIfFailed("Failed to retrieve the state of the WebRTC");
+
+                Debug.Assert(Enum.IsDefined(typeof(WebRTCIceGatheringState), state));
+
+                return state;
+            }
+        }
+
+        /// <summary>
+        /// Gets the signaling state of the WebRTC.
+        /// </summary>
+        /// <value>The current signaling state of the WebRTC.</value>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCSignalingState SignalingState
+        {
+            get
+            {
+                ValidateNotDisposed();
+
+                NativeWebRTC.GetSignalingState(Handle, out WebRTCSignalingState state).
+                    ThrowIfFailed("Failed to retrieve the state of the WebRTC");
+
+                Debug.Assert(Enum.IsDefined(typeof(WebRTCSignalingState), state));
+
+                return state;
+            }
+        }
+
+        /// <summary>
+        /// Gets the peer connection state of the WebRTC.
+        /// </summary>
+        /// <value>The current peer connection state of the WebRTC.</value>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCPeerConnectionState PeerConnectionState
+        {
+            get
+            {
+                ValidateNotDisposed();
+
+                NativeWebRTC.GetPeerConnectionState(Handle, out WebRTCPeerConnectionState state).
+                    ThrowIfFailed("Failed to retrieve the state of the WebRTC");
+
+                Debug.Assert(Enum.IsDefined(typeof(WebRTCPeerConnectionState), state));
+
+                return state;
+            }
+        }
+
+        /// <summary>
+        /// Gets the ICE connection state of the WebRTC.
+        /// </summary>
+        /// <value>The current ICE connection state of the WebRTC.</value>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCIceConnectionState IceConnectionState
+        {
+            get
+            {
+                ValidateNotDisposed();
+
+                NativeWebRTC.GetIceConnectionState(Handle, out WebRTCIceConnectionState state).
+                    ThrowIfFailed("Failed to retrieve the state of the WebRTC");
+
+                Debug.Assert(Enum.IsDefined(typeof(WebRTCIceConnectionState), state));
+
+                return state;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the STUN server url.
+        /// </summary>
+        /// <value>The STUN server url</value>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public string StunServer
+        {
+            get
+            {
+                ValidateNotDisposed();
+
+                NativeWebRTC.GetStunServer(Handle, out string server).
+                    ThrowIfFailed("Failed to get stun server name");
+
+                return server;
+            }
+            set
+            {
+                ValidateNotDisposed();
+
+                if (value == null)
+                {
+                    throw new ArgumentNullException(nameof(value), "Stun server name is null.");
+                }
+
+                NativeWebRTC.SetStunServer(Handle, value).
+                    ThrowIfFailed("Failed to set stun server name");
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the ICE transport policy.
+        /// </summary>
+        /// <value>The policy of ICE transport</value>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public IceTransportPolicy IceTransportPolicy
+        {
+            get
+            {
+                ValidateNotDisposed();
+
+                NativeWebRTC.GetIceTransportPolicy(Handle, out IceTransportPolicy policy).
+                    ThrowIfFailed("Failed to get ICE transport policy");
+
+                return policy;
+            }
+            set
+            {
+                ValidateNotDisposed();
+
+                NativeWebRTC.SetIceTransportPolicy(Handle, value).
+                    ThrowIfFailed("Failed to set ICE transport policy");
+            }
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTC.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTC.cs
new file mode 100755 (executable)
index 0000000..53b9844
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Threading.Tasks;
+using System.Collections.ObjectModel;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using Tizen.Applications;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    internal static class WebRTCLog
+    {
+        internal const string Tag = "Tizen.Multimedia.WebRTC";
+    }
+
+    /// <summary>
+    /// Provides the ability to control WebRTC.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public partial class WebRTC : IDisposable
+    {
+        private readonly WebRTCHandle _handle;
+        private List<MediaSource> _source;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebRTC"/> class.
+        /// </summary>
+        /// <feature>http://tizen.org/feature/network.wifi</feature>
+        /// <feature>http://tizen.org/feature/network.telephony</feature>
+        /// <feature>http://tizen.org/feature/network.ethernet</feature>
+        /// <privilege>http://tizen.org/privilege/internet</privilege>
+        /// <exception cref="UnauthorizedAccessException">Thrown when the permission is denied.</exception>
+        /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTC()
+        {
+            if (!Features.IsSupported(WebRTCFeatures.Wifi) &&
+                !Features.IsSupported(WebRTCFeatures.Telephony) &&
+                !Features.IsSupported(WebRTCFeatures.Ethernet))
+            {
+                throw new NotSupportedException("Network features are not supported.");
+            }
+
+            NativeWebRTC.Create(out _handle).ThrowIfFailed("Failed to create webrtc");
+
+            Debug.Assert(_handle != null);
+
+            RegisterEvents();
+
+            _source = new List<MediaSource>();
+        }
+
+        internal void ValidateWebRTCState(params WebRTCState[] desiredStates)
+        {
+            Debug.Assert(desiredStates.Length > 0);
+
+            ValidateNotDisposed();
+
+            WebRTCState curState = State;
+            if (!curState.IsAnyOf(desiredStates))
+            {
+                throw new InvalidOperationException("The WebRTC is not in a valid state. " +
+                    $"Current State : { curState }, Valid State : { string.Join(", ", desiredStates) }.");
+            }
+        }
+
+        #region Dispose support
+        private bool _disposed;
+
+        /// <summary>
+        /// Releases all resources used by the current instance.
+        /// </summary>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Releases the unmanaged resources used by the <see cref="WebRTC"/>.
+        /// </summary>
+        /// <param name="disposing">
+        /// true to release both managed and unmanaged resources;
+        /// false to release only unmanaged resources.
+        /// </param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed || !disposing)
+                return;
+
+            if (_source != null && _source.Count > 0)
+            {
+                try
+                {
+                    foreach (var source in _source)
+                    {
+                        source.DetachFrom(this);
+                    }
+                    _source = null;
+                }
+                catch (Exception ex)
+                {
+                    Log.Error(WebRTCLog.Tag, ex.ToString());
+                }
+            }
+            if (_handle != null)
+            {
+                _handle.Dispose();
+                _disposed = true;
+            }
+        }
+
+        internal void ValidateNotDisposed()
+        {
+            if (_disposed)
+            {
+                Log.Warn(WebRTCLog.Tag, "WebRTC was disposed");
+                throw new ObjectDisposedException(nameof(WebRTC));
+            }
+        }
+
+        internal bool IsDisposed => _disposed;
+        #endregion
+
+        /// <summary>
+        /// Starts the WebRTC.
+        /// </summary>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Idle"/> state.<br/>
+        /// The WebRTC state will be <see cref="WebRTCState.Negotiating"/> state.<br/>
+        /// <see cref="StateChanged"/> event will be invoked when the state is changed to <see cref="WebRTCState.Negotiating"/> internally.
+        /// </remarks>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <see also="WebRTCState"/>
+        /// <see also="StateChanged"/>
+        /// <see also="CreateOffer"/>
+        /// <see also="CreateSetOffer"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void Start()
+        {
+            ValidateWebRTCState(WebRTCState.Idle);
+
+            NativeWebRTC.Start(Handle).ThrowIfFailed("Failed to start the WebRTC");
+        }
+
+        /// <summary>
+        /// Starts the WebRTC.
+        /// </summary>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Idle"/> state.<br/>
+        /// The WebRTC state will be <see cref="WebRTCState.Negotiating"/> state.<br/>
+        /// This ensures that <see cref="StateChanged" /> event will be invoked with <see cref="WebRTCState.Negotiating"/> state.
+        /// </remarks>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <see also="WebRTCState"/>
+        /// <see also="CreateOffer"/>
+        /// <see also="CreateSetOffer"/>
+        /// <since_tizen> 9 </since_tizen>
+        public async Task StartAsync()
+        {
+            ValidateWebRTCState(WebRTCState.Idle);
+
+            var tcs = new TaskCompletionSource<bool>();
+            EventHandler<WebRTCStateChangedEventArgs> stateChangedEventHandler = (s, e) =>
+            {
+                if (e.Current == WebRTCState.Negotiating)
+                {
+                    tcs.TrySetResult(true);
+                }
+            };
+
+            try
+            {
+                StateChanged += stateChangedEventHandler;
+
+                NativeWebRTC.Start(Handle).ThrowIfFailed("Failed to start the WebRTC");
+
+                await tcs.Task;
+            }
+            finally
+            {
+                StateChanged -= stateChangedEventHandler;
+            }
+        }
+
+        /// <summary>
+        /// Stops the WebRTC.
+        /// </summary>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Negotiating"/> or <see cref="WebRTCState.Playing"/> state.<br/>
+        /// The WebRTC state will be <see cref="WebRTCState.Idle"/> state.
+        /// </remarks>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public void Stop()
+        {
+            ValidateWebRTCState(WebRTCState.Negotiating, WebRTCState.Playing);
+
+            NativeWebRTC.Stop(Handle).ThrowIfFailed("Failed to stop the WebRTC");
+        }
+
+        /// <summary>
+        /// Creates SDP offer to start a new WebRTC connection to a remote peer.
+        /// </summary>
+        /// <remarks>The WebRTC must be in the <see cref="WebRTCState.Negotiating"/></remarks>
+        /// <returns>The SDP offer.</returns>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <seealso cref="CreateOffer(Bundle)"/>
+        /// <since_tizen> 9 </since_tizen>
+        public string CreateOffer() => CreateOffer(null);
+
+        /// <summary>
+        /// Creates SDP offer with option to start a new WebRTC connection to a remote peer.
+        /// </summary>
+        /// <remarks>The WebRTC must be in the <see cref="WebRTCState.Negotiating"/></remarks>
+        /// <param name="bundle">Configuration options for the offer.</param>
+        /// <returns>The SDP offer.</returns>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <seealso cref="CreateOffer()"/>
+        /// <since_tizen> 9 </since_tizen>
+        public string CreateOffer(Bundle bundle)
+        {
+            ValidateWebRTCState(WebRTCState.Negotiating);
+
+            var bundle_ = bundle?.SafeBundleHandle ?? new SafeBundleHandle();
+            NativeWebRTC.CreateSDPOffer(Handle, bundle_, out string offer).ThrowIfFailed("Failed to create offer");
+
+            return offer;
+        }
+
+        /// <summary>
+        /// Creates SDP answer to an offer received from a remote peer.
+        /// </summary>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Negotiating"/>.<br/>
+        /// The SDP offer must be set by <see cref="SetRemoteDescription"/> before creating answer.
+        /// </remarks>
+        /// <returns>The SDP answer.</returns>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <seealso cref="CreateAnswer(Bundle)"/>
+        /// <seealso cref="SetRemoteDescription(string)"/>
+        /// <since_tizen> 9 </since_tizen>
+        public string CreateAnswer() => CreateAnswer(null);
+
+        /// <summary>
+        /// Creates SDP answer with option to an offer received from a remote peer.
+        /// </summary>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Negotiating"/>.<br/>
+        /// The SDP offer must be set by <see cref="SetRemoteDescription"/> before creating answer.
+        /// </remarks>
+        /// <param name="bundle">Configuration options for the answer.</param>
+        /// <returns>The SDP answer.</returns>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <seealso cref="CreateAnswer()"/>
+        /// <seealso cref="SetRemoteDescription(string)"/>
+        /// <since_tizen> 9 </since_tizen>
+        public string CreateAnswer(Bundle bundle)
+        {
+            ValidateWebRTCState(WebRTCState.Negotiating);
+
+            var bundle_ = bundle?.SafeBundleHandle ?? new SafeBundleHandle();
+
+            NativeWebRTC.CreateSDPAnswer(Handle, bundle_, out string answer).ThrowIfFailed("Failed to create answer");
+
+            return answer;
+        }
+
+        /// <summary>
+        /// Sets the session description for a local peer.
+        /// </summary>
+        /// <remarks>The WebRTC must be in the <see cref="WebRTCState.Negotiating"/>.</remarks>
+        /// <param name="description">The local session description.</param>
+        /// <exception cref="ArgumentException">The description is empty string.</exception>
+        /// <exception cref="ArgumentNullException">The description is null.</exception>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <seealso cref="CreateOffer()"/>
+        /// <seealso cref="CreateAnswer()"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void SetLocalDescription(string description)
+        {
+            ValidateWebRTCState(WebRTCState.Negotiating);
+
+            ValidationUtil.ValidateIsNullOrEmpty(description, nameof(description));
+
+            NativeWebRTC.SetLocalDescription(Handle, description).ThrowIfFailed("Failed to set description.");
+        }
+
+        /// <summary>
+        /// Sets the session description of the remote peer's current offer or answer.
+        /// </summary>
+        /// <remarks>The WebRTC must be in the <see cref="WebRTCState.Negotiating"/>.</remarks>
+        /// <param name="description">The remote session description.</param>
+        /// <exception cref="ArgumentException">The description is empty string.</exception>
+        /// <exception cref="ArgumentNullException">The description is null.</exception>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <seealso cref="CreateOffer()"/>
+        /// <seealso cref="CreateAnswer()"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void SetRemoteDescription(string description)
+        {
+            ValidateWebRTCState(WebRTCState.Negotiating);
+
+            ValidationUtil.ValidateIsNullOrEmpty(description, nameof(description));
+
+            NativeWebRTC.SetRemoteDescription(Handle, description).ThrowIfFailed("Failed to set description.");
+        }
+
+        /// <summary>
+        /// Adds a new ICE candidate from the remote peer over its signaling channel.
+        /// </summary>
+        /// <remarks>The WebRTC must be in the <see cref="WebRTCState.Negotiating"/>.</remarks>
+        /// <param name="iceCandidate">The ICE candidate.</param>
+        /// <exception cref="ArgumentException">The ICE candidate is empty string.</exception>
+        /// <exception cref="ArgumentNullException">The ICE candidate is null.</exception>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public void AddIceCandidate(string iceCandidate)
+        {
+            ValidateWebRTCState(WebRTCState.Negotiating);
+
+            ValidationUtil.ValidateIsNullOrEmpty(iceCandidate, nameof(iceCandidate));
+
+            NativeWebRTC.AddIceCandidate(Handle, iceCandidate).ThrowIfFailed("Failed to set ICE candidate.");
+        }
+
+        /// <summary>
+        /// Adds new ICE candidates from the remote peer over its signaling channel.
+        /// </summary>
+        /// <remarks>The WebRTC must be in the <see cref="WebRTCState.Negotiating"/>.</remarks>
+        /// <param name="iceCandidates">The ICE candidates.</param>
+        /// <exception cref="ArgumentException">The ICE candidate is empty string.</exception>
+        /// <exception cref="ArgumentNullException">The ICE candidate is null.</exception>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public void AddIceCandidates(IEnumerable<string> iceCandidates)
+        {
+            ValidateWebRTCState(WebRTCState.Negotiating);
+
+            ValidationUtil.ValidateIsAny(iceCandidates);
+
+            #pragma warning disable CA1062
+            foreach (string iceCandidate in iceCandidates)
+            {
+                AddIceCandidate(iceCandidate);
+            }
+            #pragma warning restore CA1062
+        }
+
+        /// <summary>
+        /// Adds media source.
+        /// </summary>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Idle"/>.<br/>
+        /// Each MediaSource requires different feature or privilege.<br/>
+        /// <see cref="MediaCameraSource"/> needs camera feature and privilege.<br/>
+        /// <see cref="MediaFileSource"/> needs mediastorage or externalstorage privilege.<br/>
+        /// <see cref="MediaMicrophoneSource"/> needs microphone feature and recorder privilege.<br/>
+        /// </remarks>
+        /// <param name="source">The media sources to add.</param>
+        /// <feature>http://tizen.org/feature/camera</feature>
+        /// <feature>http://tizen.org/feature/microphone</feature>
+        /// <privilege>http://tizen.org/privilege/camera</privilege>
+        /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
+        /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
+        /// <privilege>http://tizen.org/privilege/recorder</privilege>
+        /// <exception cref="ArgumentNullException">The media source is null.</exception>
+        /// <exception cref="InvalidOperationException">
+        /// The WebRTC is not in the valid state.<br/>
+        /// - or -<br/>
+        /// All or one of <paramref name="source"/> was already detached.
+        /// </exception>
+        /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <exception cref="UnauthorizedAccessException">Thrown when the permission is denied.</exception>
+        /// <seealso cref="MediaCameraSource"/>
+        /// <seealso cref="MediaMicrophoneSource"/>
+        /// <seealso cref="MediaTestSource"/>
+        /// <seealso cref="MediaPacketSource"/>
+        /// <seealso cref="AddSources"/>
+        /// <seealso cref="RemoveSource"/>
+        /// <seealso cref="RemoveSources"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void AddSource(MediaSource source)
+        {
+            if (source == null)
+            {
+                throw new ArgumentNullException(nameof(source), "source is null");
+            }
+
+            ValidateWebRTCState(WebRTCState.Idle);
+
+            source?.AttachTo(this);
+
+            _source.Add(source);
+        }
+
+        /// <summary>
+        /// Adds media sources.
+        /// </summary>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Idle"/>.<br/>
+        /// Each MediaSource requires different feature or privilege.<br/>
+        /// <see cref="MediaCameraSource"/> needs camera feature and privilege.<br/>
+        /// <see cref="MediaFileSource"/> needs mediastorage or externalstorage privilege.<br/>
+        /// <see cref="MediaMicrophoneSource"/> needs microphone feature and recorder privilege.<br/>
+        /// </remarks>
+        /// <param name="sources">The media sources to add.</param>
+        /// <feature>http://tizen.org/feature/camera</feature>
+        /// <feature>http://tizen.org/feature/microphone</feature>
+        /// <privilege>http://tizen.org/privilege/camera</privilege>
+        /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
+        /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
+        /// <privilege>http://tizen.org/privilege/recorder</privilege>
+        /// <exception cref="ArgumentNullException">The media source is null.</exception>
+        /// <exception cref="InvalidOperationException">
+        /// The WebRTC is not in the valid state.<br/>
+        /// - or -<br/>
+        /// All or one of <paramref name="sources"/> was already detached.
+        /// </exception>
+        /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <exception cref="UnauthorizedAccessException">Thrown when the permission is denied.</exception>
+        /// <seealso cref="MediaCameraSource"/>
+        /// <seealso cref="MediaMicrophoneSource"/>
+        /// <seealso cref="MediaTestSource"/>
+        /// <seealso cref="MediaPacketSource"/>
+        /// <seealso cref="AddSource"/>
+        /// <seealso cref="RemoveSource"/>
+        /// <seealso cref="RemoveSources"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void AddSources(params MediaSource[] sources)
+        {
+            foreach (var source in sources)
+            {
+                AddSource(source);
+            }
+        }
+
+        /// <summary>
+        /// Removes media source.
+        /// </summary>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Idle"/>.<br/>
+        /// If user want to use removed MediaSource again, user should create new instance for it.
+        /// </remarks>
+        /// <param name="source">The media source to remove.</param>
+        /// <exception cref="ArgumentNullException">The media source is null.</exception>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <seealso cref="MediaCameraSource"/>
+        /// <seealso cref="MediaMicrophoneSource"/>
+        /// <seealso cref="MediaTestSource"/>
+        /// <seealso cref="MediaPacketSource"/>
+        /// <seealso cref="AddSource"/>
+        /// <seealso cref="AddSources"/>
+        /// <seealso cref="RemoveSources"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void RemoveSource(MediaSource source)
+        {
+            if (source == null)
+            {
+                throw new ArgumentNullException(nameof(source), "source is null");
+            }
+
+            ValidateWebRTCState(WebRTCState.Idle);
+
+            source?.DetachFrom(this);
+
+            _source.Remove(source);
+
+            source = null;
+        }
+
+        /// <summary>
+        /// Removes media sources.
+        /// </summary>
+        /// <remarks>
+        /// The WebRTC must be in the <see cref="WebRTCState.Idle"/>.<br/>
+        /// If user want to use removed MediaSource again, user should create new instance for it.
+        /// </remarks>
+        /// <param name="sources">The media source to remove.</param>
+        /// <exception cref="ArgumentNullException">The media source is null.</exception>
+        /// <exception cref="InvalidOperationException">The WebRTC is not in the valid state.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <seealso cref="MediaCameraSource"/>
+        /// <seealso cref="MediaMicrophoneSource"/>
+        /// <seealso cref="MediaTestSource"/>
+        /// <seealso cref="MediaPacketSource"/>
+        /// <seealso cref="AddSource"/>
+        /// <seealso cref="AddSources"/>
+        /// <seealso cref="RemoveSource"/>
+        /// <since_tizen> 9 </since_tizen>
+        public void RemoveSources(params MediaSource[] sources)
+        {
+            foreach (var source in sources)
+            {
+                RemoveSource(source);
+            }
+        }
+
+        /// <summary>
+        /// Sets a turn server.
+        /// </summary>
+        /// <exception cref="ArgumentNullException">The <paramref name="turnServer"/> is null.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public void SetTurnServer(string turnServer)
+        {
+            ValidateNotDisposed();
+
+            if (turnServer == null)
+            {
+                throw new ArgumentNullException(nameof(turnServer), "Turn server name is null.");
+            }
+
+            NativeWebRTC.AddTurnServer(Handle, turnServer).
+                ThrowIfFailed("Failed to add turn server");
+        }
+
+        /// <summary>
+        /// Sets turn servers.
+        /// </summary>
+        /// <exception cref="ArgumentNullException">The one of <paramref name="turnServers"/> is null.</exception>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public void SetTurnServers(params string[] turnServers)
+        {
+            ValidateNotDisposed();
+
+            foreach (var turnServer in turnServers)
+            {
+                SetTurnServer(turnServer);
+            }
+        }
+
+        /// <summary>
+        /// Gets all turn servers.
+        /// </summary>
+        /// <returns>The turn server list.</returns>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public ReadOnlyCollection<string> GetTurnServer()
+        {
+            ValidateNotDisposed();
+
+            var list = new List<string>();
+
+            NativeWebRTC.RetrieveTurnServerCallback callback = (server, _) =>
+            {
+                if (!string.IsNullOrWhiteSpace(server))
+                {
+                    list.Add(server);
+                }
+
+                return true;
+            };
+
+            NativeWebRTC.ForeachTurnServer(Handle, callback).ThrowIfFailed("Failed to retrieve turn server");
+
+            return list.AsReadOnly();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannel.Events.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannel.Events.cs
new file mode 100755 (executable)
index 0000000..2c277f9
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides the ability to control WebRTC data channel.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public partial class WebRTCDataChannel
+    {
+        private NativeDataChannel.OpenedCallback _webRtcDataChannelOpenedCallback;
+        private NativeDataChannel.ClosedCallback _webRtcDataChannelClosedCallback;
+        private NativeDataChannel.MessageReceivedCallback _webRtcDataChannelMsgRecvCallback;
+        private NativeDataChannel.ErrorOccurredCallback _webRtcDataChannelErrorOccurredCallback;
+
+        /// <summary>
+        /// Occurs when the data channel's underlying data transport is established.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<EventArgs> Opened;
+
+        /// <summary>
+        /// Occurs when the data channel has closed down.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<EventArgs> Closed;
+
+        /// <summary>
+        /// Occurs when a message is received from the remote peer.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCDataChannelMessageReceivedEventArgs> MessageReceived;
+
+        /// <summary>
+        /// Occurs when an error occurs on the data channel.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public event EventHandler<WebRTCDataChannelErrorOccurredEventArgs> ErrorOccurred;
+
+        private void RegisterEvents()
+        {
+            RegisterDataChannelOpenedCallback();
+            RegisterDataChannelClosedCallback();
+            RegisterDataChannelMsgRecvCallback();
+            RegisterDataChannelErrorOccurredCallback();
+        }
+
+        private void RegisterDataChannelOpenedCallback()
+        {
+            _webRtcDataChannelOpenedCallback = (dataChannelHandle, _) =>
+            {
+                Opened?.Invoke(this, new EventArgs());
+            };
+
+            NativeDataChannel.SetOpenedCb(_handle, _webRtcDataChannelOpenedCallback).
+                ThrowIfFailed("Failed to set data channel opened callback.");
+        }
+
+        private void RegisterDataChannelMsgRecvCallback()
+        {
+            _webRtcDataChannelMsgRecvCallback = (dataChannelHandle, type, message, _) =>
+            {
+                MessageReceived?.Invoke(this, new WebRTCDataChannelMessageReceivedEventArgs(type, message));
+            };
+
+            NativeDataChannel.SetMessageReceivedCb(_handle, _webRtcDataChannelMsgRecvCallback).
+                ThrowIfFailed("Failed to set data channel message received callback.");
+        }
+
+        private void RegisterDataChannelErrorOccurredCallback()
+        {
+            _webRtcDataChannelErrorOccurredCallback = (dataChannelHandle, error, _) =>
+            {
+                ErrorOccurred?.Invoke(this, new WebRTCDataChannelErrorOccurredEventArgs((WebRTCError)error));
+            };
+
+            NativeDataChannel.SetErrorOccurredCb(_handle, _webRtcDataChannelErrorOccurredCallback).
+                ThrowIfFailed("Failed to set data channel error callback.");
+        }
+
+        private void RegisterDataChannelClosedCallback()
+        {
+            _webRtcDataChannelClosedCallback = (dataChannelHandle, _) =>
+            {
+                Closed?.Invoke(this, new EventArgs());
+            };
+
+            NativeDataChannel.SetClosedCb(_handle, _webRtcDataChannelClosedCallback).
+                ThrowIfFailed("Failed to set data channel closed callback.");
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannel.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannel.cs
new file mode 100755 (executable)
index 0000000..3a8a5c9
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using Tizen.Applications;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides the ability to control WebRTC data channel.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public partial class WebRTCDataChannel : IDisposable
+    {
+        private readonly IntPtr _handle;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebRTCDataChannel"/> class.
+        /// </summary>
+        /// <param name="webRtc">The owner of this WebRTCDataChannel.</param>
+        /// <param name="label">The name of this data channel.</param>
+        /// <exception cref="ArgumentNullException">The webRtc or label is null.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCDataChannel(WebRTC webRtc, string label)
+            : this(webRtc, label, null)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebRTCDataChannel"/> class.
+        /// </summary>
+        /// <remarks>
+        /// The bundle is similar format as the RTCDataChannelInit members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit.<br/>
+        /// The following attributes can be set to options by using <see cref="Bundle"/> API:<br/>
+        /// 'ordered' of type bool            : Whether the channel will send data with guaranteed ordering. The default value is true.<br/>
+        /// 'max-packet-lifetime' of type int : The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset. The default value is -1.<br/>
+        /// 'max-retransmits' of type int     : The number of times data will be attempted to be transmitted without acknowledgement before dropping. The default value is -1.<br/>
+        /// 'protocol' of type string         : The subprotocol used by this channel. The default value is NULL.<br/>
+        /// 'id' of type int                  : Override the default identifier selection of this channel. The default value is -1.<br/>
+        /// 'priority' of type int            : The priority to use for this channel(1:very low, 2:low, 3:medium, 4:high). The default value is 2.<br/>
+        /// </remarks>
+        /// <param name="webRtc">The owner of this WebRTCDataChannel.</param>
+        /// <param name="label">The name of this data channel.</param>
+        /// <param name="bundle">The data channel option.</param>
+        /// <exception cref="ArgumentNullException">The webRtc or label is null.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCDataChannel(WebRTC webRtc, string label, Bundle bundle)
+        {
+            if (webRtc == null)
+            {
+                throw new ArgumentNullException(nameof(webRtc), "WebRTC is not created successfully.");
+            }
+
+            if (string.IsNullOrEmpty(label))
+            {
+                throw new ArgumentNullException(nameof(label), "label is null.");
+            }
+
+            var bundle_ = bundle?.SafeBundleHandle ?? new SafeBundleHandle();
+            NativeDataChannel.Create(webRtc.Handle, label, bundle_, out _handle).
+                ThrowIfFailed("Failed to create webrtc data channel");
+
+            Debug.Assert(_handle != null);
+
+            Label = label;
+
+            RegisterEvents();
+        }
+
+        internal WebRTCDataChannel(IntPtr dataChannelHandle)
+        {
+            if (dataChannelHandle == IntPtr.Zero)
+            {
+                throw new ArgumentNullException(nameof(dataChannelHandle),
+                    "WebRTC is not created successfully in native");
+            }
+
+            _handle = dataChannelHandle;
+
+            NativeDataChannel.GetLabel(_handle, out string label).
+                ThrowIfFailed("Failed to get label");
+
+            Label = label;
+
+            Log.Info(WebRTCLog.Tag, "Register event");
+            RegisterEvents();
+        }
+
+        private IntPtr Handle
+        {
+            get
+            {
+                ValidateNotDisposed();
+                return _handle;
+            }
+        }
+
+        /// <summary>
+        /// Gets the label of this data channel.
+        /// </summary>
+        /// <value>The label.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public string Label { get; }
+
+        /// <summary>
+        /// Sends a string data across the data channel to the remote peer.
+        /// </summary>
+        /// <param name="data">The string data to send</param>
+        /// <since_tizen> 9 </since_tizen>
+        public void Send(string data)
+        {
+            ValidateNotDisposed();
+
+            NativeDataChannel.SendString(Handle, data).
+                ThrowIfFailed("Failed to send string data");
+        }
+
+        /// <summary>
+        /// Sends byte data across the data channel to the remote peer.
+        /// </summary>
+        /// <param name="data">The byte data to send</param>
+        /// <since_tizen> 9 </since_tizen>
+        public void Send(byte[] data)
+        {
+            ValidateNotDisposed();
+
+            if (data == null)
+            {
+                throw new ArgumentNullException(nameof(data), "data is null");
+            }
+
+            NativeDataChannel.SendBytes(Handle, data, (uint)data.Length).
+                ThrowIfFailed("Failed to send bytes data");
+        }
+
+        #region Dispose support
+        private bool _disposed;
+
+        /// <summary>
+        /// Releases all resources used by the current instance.
+        /// </summary>
+        /// <exception cref="ObjectDisposedException">The WebRTC has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Releases the unmanaged resources used by the <see cref="WebRTCDataChannel"/>.
+        /// </summary>
+        /// <param name="disposing">
+        /// true to release both managed and unmanaged resources;
+        /// false to release only unmanaged resources.
+        /// </param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed || !disposing)
+            {
+                return;
+            }
+
+            if (true)
+            {
+                NativeDataChannel.Destroy(_handle);
+                _disposed = true;
+            }
+        }
+
+        private void ValidateNotDisposed()
+        {
+            if (_disposed)
+            {
+                Log.Warn(WebRTCLog.Tag, "WebRTCDataChannel was disposed");
+                throw new ObjectDisposedException(nameof(WebRTCDataChannel));
+            }
+        }
+        #endregion Dispose support
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannelErrorOccurredEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannelErrorOccurredEventArgs.cs
new file mode 100755 (executable)
index 0000000..ddae8b4
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTCDataChannel.ErrorOccurred"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCDataChannelErrorOccurredEventArgs : EventArgs
+    {
+        internal WebRTCDataChannelErrorOccurredEventArgs(WebRTCError error)
+        {
+            Error = error;
+        }
+
+        /// <summary>
+        /// Gets the error.
+        /// </summary>
+        /// <value>The error.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCError Error { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"Error={Error}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannelEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannelEventArgs.cs
new file mode 100755 (executable)
index 0000000..479190b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.DataChannel"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCDataChannelEventArgs : EventArgs
+    {
+        internal WebRTCDataChannelEventArgs(IntPtr dataChannelHandle)
+        {
+            DataChannel = new WebRTCDataChannel(dataChannelHandle);
+        }
+
+        /// <summary>
+        /// Gets the created data channel instance.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCDataChannel DataChannel { get; }
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannelMessageReceivedEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCDataChannelMessageReceivedEventArgs.cs
new file mode 100755 (executable)
index 0000000..b3aa86a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Runtime.InteropServices;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTCDataChannel.MessageReceived"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCDataChannelMessageReceivedEventArgs : EventArgs
+    {
+        internal WebRTCDataChannelMessageReceivedEventArgs(DataChannelType type, IntPtr message)
+        {
+            Type = type;
+
+            if (type == DataChannelType.Strings)
+            {
+                Message = Marshal.PtrToStringAnsi(message);
+            }
+            else
+            {
+                NativeDataChannel.GetData(message, out IntPtr data, out ulong size).
+                    ThrowIfFailed("Failed to get data");
+
+                Data = new byte[(int)size];
+                Marshal.Copy(data, Data, 0, (int)size);
+            }
+        }
+
+        /// <summary>
+        /// Gets the data channel type.
+        /// </summary>
+        /// <value>The data channel type.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public DataChannelType Type { get; }
+
+        /// <summary>
+        /// Gets the string message from remote peer.
+        /// </summary>
+        /// <remarks>
+        /// If <see cref="WebRTCDataChannelMessageReceivedEventArgs.Type"/> is <see cref="DataChannelType.Bytes"/>, this property is null.
+        /// </remarks>
+        /// <value>The message.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public string Message { get; }
+
+        #pragma warning disable CA1819 // the purpose of this member is to pass received data to user, no need to protect it from changes
+        /// <summary>
+        /// Gets the byte data from remote peer.
+        /// </summary>
+        /// <remarks>
+        /// If <see cref="WebRTCDataChannelMessageReceivedEventArgs.Type"/> is <see cref="DataChannelType.Strings"/>, this property is null.
+        /// </remarks>
+        /// <value>The message.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public byte[] Data { get; }
+        #pragma warning restore CA1819
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"Channel type={Type}, Message={Message}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCEnums.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCEnums.cs
new file mode 100755 (executable)
index 0000000..d5f6508
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Specifies errors.
+    /// </summary>
+    /// <seealso cref="WebRTC.ErrorOccurred"/>
+    /// <seealso cref="WebRTCErrorOccurredEventArgs"/>
+    /// <since_tizen> 9 </since_tizen>
+    public enum WebRTCError
+    {
+        /// <summary>
+        /// The connection failed.
+        /// </summary>
+        ConnectionFailed = WebRTCErrorCode.ConnectionFailed,
+
+        /// <summary>
+        /// The stream failed.
+        /// </summary>
+        StreamFailed = WebRTCErrorCode.StreamFailed,
+
+        /// <summary>
+        /// The resource failed.
+        /// </summary>
+        ResourceFailed = WebRTCErrorCode.ResourceFailed,
+
+        /// <summary>
+        /// The resource conflicted.
+        /// </summary>
+        ResourceConflict = WebRTCErrorCode.ResourceConflict,
+
+        /// <summary>
+        /// The invalid operation.
+        /// </summary>
+        InvalidOperation = WebRTCErrorCode.InvalidOperation
+    }
+
+    /// <summary>
+    /// Specifies states that a <see cref="WebRTC"/> can have.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public enum WebRTCState
+    {
+        /// <summary>
+        /// The Initial state, create but not started.
+        /// </summary>
+        Idle,
+
+        /// <summary>
+        /// Started and negotiating.
+        /// </summary>
+        Negotiating,
+
+        /// <summary>
+        /// Negotiated and started all streams.
+        /// </summary>
+        Playing,
+    }
+
+    /// <summary>
+    /// Specifies ICE gathering states that a <see cref="WebRTC"/> can have.
+    /// </summary>
+    /// <seealso cref="WebRTC.IceCandidate"/>
+    /// <since_tizen> 9 </since_tizen>
+    public enum WebRTCIceGatheringState
+    {
+        /// <summary>
+        /// The Initial state.
+        /// </summary>
+        New,
+
+        /// <summary>
+        /// Ice candidate is creating.
+        /// </summary>
+        Gathering,
+
+        /// <summary>
+        /// Ice gathering sequence has been completed.
+        /// </summary>
+        Completed,
+    }
+
+    /// <summary>
+    /// Specifies signaling states that a <see cref="WebRTC"/> can have.
+    /// </summary>
+    /// <remarks>This state is related in SDP offer/answer.</remarks>
+    /// <seealso cref="WebRTC.SetLocalDescription"/>
+    /// <seealso cref="WebRTC.SetRemoteDescription"/>
+    /// <seealso cref="WebRTC.CreateAnswer()"/>
+    /// <since_tizen> 9 </since_tizen>
+    public enum WebRTCSignalingState
+    {
+        /// <summary>
+        /// The Initial state.
+        /// </summary>
+        Stable,
+
+        /// <summary>
+        /// The local SDP offer has been applied successfully.
+        /// </summary>
+        HaveLocalOffer,
+
+        /// <summary>
+        /// The remote SDP offer has been applied successfully.
+        /// </summary>
+        HaveRemoteOffer,
+
+        /// <summary>
+        /// The SDP offer sent by the remote peer has been applied and <br/>
+        /// an answer has been created and applied.
+        /// </summary>
+        HaveLocalPrAnswer,
+
+        /// <summary>
+        /// A provisional answer has been received and successfully applied in local.
+        /// </summary>
+        HaveRemotePrAnswer,
+
+        /// <summary>
+        /// The connection is closed.
+        /// </summary>
+        Closed
+    }
+
+    /// <summary>
+    /// Specifies peer connection states that a <see cref="WebRTC"/> can have.
+    /// </summary>
+    /// <remarks>This state is related in peer connection.</remarks>
+    /// <since_tizen> 9 </since_tizen>
+    public enum WebRTCPeerConnectionState
+    {
+        /// <summary>
+        /// The Initial state.
+        /// </summary>
+        New,
+
+        /// <summary>
+        /// Establishing a connection is in the process.
+        /// </summary>
+        Connecting,
+
+        /// <summary>
+        /// The remote SDP offer has been applied successfully.
+        /// </summary>
+        Connected,
+
+        /// <summary>
+        /// The SDP offer sent by the remote peer has been applied and an answer has been created and applied.
+        /// </summary>
+        Disconnected,
+
+        /// <summary>
+        /// A provisional answer has been received and successfully applied in local.
+        /// </summary>
+        Failed,
+
+        /// <summary>
+        /// The connection is closed.
+        /// </summary>
+        Closed
+    }
+
+    /// <summary>
+    /// Specifies ICE connection states that a <see cref="WebRTC"/> can have.
+    /// </summary>
+    /// <remarks>This state describe the current state of local and its connection to the ICE server(STUN or TURN).</remarks>
+    /// <since_tizen> 9 </since_tizen>
+    public enum WebRTCIceConnectionState
+    {
+        /// <summary>
+        /// The Initial state.
+        /// </summary>
+        New,
+
+        /// <summary>
+        /// Checking pairs of local and remote candidates against one another to try to find a compatible match.
+        /// </summary>
+        Checking,
+
+        /// <summary>
+        /// A usable pairing of local and remote candidates has been found for all components of the connection,<br/>
+        /// and the connection has been established.
+        /// </summary>
+        Connected,
+
+        /// <summary>
+        /// Gathering candidates has been finished and hecked all pairs against one another,<br/>
+        /// and has found a connection for all components.
+        /// </summary>
+        Completed,
+
+        /// <summary>
+        /// There's no compatible matches.
+        /// </summary>
+        Failed,
+
+        /// <summary>
+        /// This is a less stringent test than "Failed" and may trigger intermittently and resolve just as spontaneously on less reliable networks,<br/>
+        /// or during temporary disconnections. When the problem resolves, the connection may return to the "connected" state.
+        /// </summary>
+        Disconnected,
+
+        /// <summary>
+        /// Closed.
+        /// </summary>
+        Closed
+    }
+
+    internal static class WebRTCStateExtensions
+    {
+        internal static bool IsAnyOf<T>(this T thisState, params T[] states) =>
+            Array.IndexOf<T>(states, thisState) != -1;
+    }
+
+    /// <summary>
+    /// Specifies data type that transfers on data channel.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public enum DataChannelType
+    {
+        /// <summary>
+        /// The string data type.
+        /// </summary>
+        Strings,
+
+        /// <summary>
+        /// The byte data type.
+        /// </summary>
+        Bytes,
+    }
+
+    /// <summary>
+    /// Specifies the buffer state type of <see cref="MediaPacketSource"/>.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public enum MediaPacketBufferStatus
+    {
+        /// <summary>
+        /// The buffer underrun.
+        /// </summary>
+        Underrun,
+
+        /// <summary>
+        /// The buffer overflow.
+        /// </summary>
+        Overflow,
+    }
+
+    /// <summary>
+    /// Specifies the media type.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public enum MediaType
+    {
+        /// <summary>
+        /// The audio type.
+        /// </summary>
+        Audio,
+
+        /// <summary>
+        /// The video type.
+        /// </summary>
+        Video,
+    }
+
+    /// <summary>
+    /// Specifies the transceiver direction type.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public enum TransceiverDirection
+    {
+        /// <summary>
+        /// Send only.
+        /// </summary>
+        SendOnly,
+
+        /// <summary>
+        /// Receive only.
+        /// </summary>
+        RecvOnly,
+
+        /// <summary>
+        /// Send and receive.
+        /// </summary>
+        SendRecv,
+    }
+
+    /// <summary>
+    /// Specifies the policy of Ice transport.
+    /// </summary>
+    /// <remarks>
+    /// See also https://www.w3.org/TR/webrtc/#rtcicetransportpolicy-enum
+    /// </remarks>
+    /// <since_tizen> 9 </since_tizen>
+    public enum IceTransportPolicy
+    {
+        /// <summary>
+        /// All.
+        /// </summary>
+        All,
+
+        /// <summary>
+        /// Relay.
+        /// </summary>
+        Relay
+    }
+
+    /// <summary>
+    /// Specifies the display type.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public enum WebRTCDisplayMode
+    {
+        /// <summary>
+        /// Letter box.
+        /// </summary>
+        LetterBox,
+
+        /// <summary>
+        /// Original size.
+        /// </summary>
+        OriginSize,
+
+        /// <summary>
+        /// Full screen.
+        /// </summary>
+        Full
+    }
+
+    /// <summary>
+    /// Specifies the signaling message type.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public enum SignalingMessageType
+    {
+        /// <summary>
+        /// Connected.
+        /// </summary>
+        Connected,
+
+        /// <summary>
+        /// Disconnected.
+        /// </summary>
+        Disconnected,
+
+        /// <summary>
+        /// Session established.
+        /// </summary>
+        SessionEstablished,
+
+        /// <summary>
+        /// Session closed.
+        /// </summary>
+        SessionClosed,
+
+        /// <summary>
+        /// SDP(Session Description Protocol).
+        /// </summary>
+        Sdp,
+
+        /// <summary>
+        /// ICE(Interactive Connectivity Establishment) candidate.
+        /// </summary>
+        IceCandidate,
+
+        /// <summary>
+        /// Error.
+        /// </summary>
+        Error,
+    }
+
+    internal enum MediaSourceType
+    {
+        AudioTest,
+
+        VideoTest,
+
+        Microphone,
+
+        Camera,
+
+        Screen,
+
+        File,
+
+        MediaPacket
+    }
+
+    internal enum CustomMediaSourceType
+    {
+        Audio = 7,
+
+        Video
+    }
+
+    internal enum WebRTCDisplayType
+    {
+        Overlay,
+
+        Evas,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCError.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCError.cs
new file mode 100755 (executable)
index 0000000..32f5bf5
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.IO;
+using Tizen.Internals.Errors;
+
+namespace Tizen.Multimedia.Remoting
+{
+    internal enum WebRTCErrorCode
+    {
+        None = ErrorCode.None,
+        FeatureNotSupported = ErrorCode.NotSupported,
+        PermissionDenied = ErrorCode.PermissionDenied,
+        InvalidArgument = ErrorCode.InvalidParameter,
+        InvalidOperation = ErrorCode.InvalidOperation,
+        TizenWebRTCError = -0x03000000,
+        InvalidState = TizenWebRTCError | 0x01,
+        ConnectionFailed = TizenWebRTCError | 0x02,
+        StreamFailed = TizenWebRTCError | 0x03,
+        ResourceFailed = TizenWebRTCError | 0x04,
+        ResourceConflict = TizenWebRTCError | 0x05
+    }
+
+    internal static class WebRTCErrorCodeExtensions
+    {
+        internal static void ThrowIfFailed(this WebRTCErrorCode errorCode, string message)
+        {
+            if (errorCode == WebRTCErrorCode.None)
+                return;
+
+            string errMessage = (message ?? "Operation failed") + " : " + errorCode.ToString() + ".";
+
+            switch (errorCode)
+            {
+                case WebRTCErrorCode.FeatureNotSupported:
+                    throw new NotSupportedException(errMessage);
+                case WebRTCErrorCode.InvalidState:
+                case WebRTCErrorCode.ConnectionFailed:
+                case WebRTCErrorCode.StreamFailed:
+                case WebRTCErrorCode.ResourceFailed:
+                case WebRTCErrorCode.ResourceConflict:
+                case WebRTCErrorCode.InvalidOperation:
+                    throw new InvalidOperationException(errMessage);
+                case WebRTCErrorCode.InvalidArgument:
+                    throw new ArgumentException(errMessage);
+                case WebRTCErrorCode.PermissionDenied:
+                    throw new UnauthorizedAccessException(errMessage);
+            }
+        }
+    }
+}
+
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCErrorOccurredEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCErrorOccurredEventArgs.cs
new file mode 100755 (executable)
index 0000000..086e69e
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.ErrorOccurred"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCErrorOccurredEventArgs : EventArgs
+    {
+        internal WebRTCErrorOccurredEventArgs(WebRTCError error, WebRTCState state)
+        {
+            Error = error;
+            State = state;
+        }
+
+        /// <summary>
+        /// Gets the error.
+        /// </summary>
+        /// <value>The error.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCError Error { get; }
+
+        /// <summary>
+        /// Gets the current state.
+        /// </summary>
+        /// <value>The state.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCState State { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"Error={Error}, State={State}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCFeatures.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCFeatures.cs
new file mode 100755 (executable)
index 0000000..b10644c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Tizen.Multimedia.Remoting
+{
+    internal static class WebRTCFeatures
+    {
+        internal const string Wifi = "http://tizen.org/feature/network.wifi";
+        internal const string Telephony = "http://tizen.org/feature/network.telephony";
+        internal const string Ethernet = "http://tizen.org/feature/network.ethernet";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCFrameEncodedEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCFrameEncodedEventArgs.cs
new file mode 100755 (executable)
index 0000000..455009c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.AudioFrameEncoded"/> or <see cref="WebRTC.VideoFrameEncoded"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCFrameEncodedEventArgs : EventArgs
+    {
+        internal WebRTCFrameEncodedEventArgs(MediaStreamTrack track, MediaPacket packet)
+        {
+            MediaStreamTrack = track;
+            Packet = packet;
+        }
+
+        /// <summary>
+        /// Gets the track information.
+        /// </summary>
+        /// <value>The media type.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaStreamTrack MediaStreamTrack { get; }
+
+        /// <summary>
+        /// Gets the media packet which has a frame data.
+        /// </summary>
+        /// <value>The media packet which has a frame data.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaPacket Packet { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"MediaType={MediaStreamTrack.Type}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCIceCandidateEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCIceCandidateEventArgs.cs
new file mode 100755 (executable)
index 0000000..3375c99
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.IceCandidate"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCIceCandidateEventArgs : EventArgs
+    {
+        internal WebRTCIceCandidateEventArgs(string iceCandidate)
+        {
+            ICECandidate = iceCandidate;
+        }
+
+        /// <summary>
+        /// Gets the ICE candidate.
+        /// </summary>
+        /// <value>The ICE candidate.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public string ICECandidate { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"ICE candidate={ICECandidate}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCIceConnectionStateChangedEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCIceConnectionStateChangedEventArgs.cs
new file mode 100755 (executable)
index 0000000..11e712a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.IceConnectionStateChanged"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCIceConnectionStateChangedEventArgs : EventArgs
+    {
+        internal WebRTCIceConnectionStateChangedEventArgs(WebRTCIceConnectionState state)
+        {
+            State = state;
+        }
+
+        /// <summary>
+        /// The ICE connection state.
+        /// </summary>
+        /// <value>The ICE connection state</value>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCIceConnectionState State { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"State={State}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCIceGatheringStateChangedEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCIceGatheringStateChangedEventArgs.cs
new file mode 100755 (executable)
index 0000000..431bac7
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.IceGatheringStateChanged"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCIceGatheringStateChangedEventArgs : EventArgs
+    {
+        internal WebRTCIceGatheringStateChangedEventArgs(WebRTCIceGatheringState state)
+        {
+            State = state;
+        }
+
+        /// <summary>
+        /// The current ICE gathering state.
+        /// </summary>
+        /// <value>The ICE gathering state.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCIceGatheringState State { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"State={State}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCPeerConnectionStateChangedEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCPeerConnectionStateChangedEventArgs.cs
new file mode 100755 (executable)
index 0000000..294d5cf
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.PeerConnectionStateChanged"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCPeerConnectionStateChangedEventArgs : EventArgs
+    {
+        internal WebRTCPeerConnectionStateChangedEventArgs(WebRTCPeerConnectionState state)
+        {
+            State = state;
+        }
+
+        /// <summary>
+        /// The peer connection state.
+        /// </summary>
+        /// <value>The peer connection state</value>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCPeerConnectionState State { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"State={State}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCSignalingEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCSignalingEventArgs.cs
new file mode 100755 (executable)
index 0000000..c6fc5d4
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTCSignalingClient.SignalingMessage"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class WebRTCSignalingEventArgs : EventArgs
+    {
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal WebRTCSignalingEventArgs(SignalingMessageType type, string message)
+        {
+            MessageType = type;
+            Message = message;
+        }
+
+        /// <summary>
+        /// Gets the signaling message type.
+        /// </summary>
+        /// <value>The signaling message type.</value>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public SignalingMessageType MessageType { get; }
+
+        /// <summary>
+        /// Gets the message from remote peer.
+        /// </summary>
+        /// <value>The message.</value>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string Message { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override string ToString() => $"MessageType={MessageType}, Message={Message}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCSignalingServer.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCSignalingServer.cs
new file mode 100755 (executable)
index 0000000..24d0277
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using static Interop;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides the ability to control WebRTCSignalingServer.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class WebRTCSignalingServer : IDisposable
+    {
+        private readonly IntPtr _handle;
+        private bool _disposed;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebRTCSignalingServer"/> class.
+        /// </summary>
+        /// <param name="port">The server port.</param>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public WebRTCSignalingServer(int port)
+        {
+            SignalingServer.Create(port, out _handle).
+                ThrowIfFailed("Failed to create signaling");
+
+            Debug.Assert(true);
+        }
+
+        /// <summary>
+        /// Starts the signaling server.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Start()
+        {
+            ValidateNotDisposed();
+
+            SignalingServer.Start(_handle).
+                ThrowIfFailed("Failed to start signaling server");
+        }
+
+        /// <summary>
+        /// Stops the signaling server.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Stop()
+        {
+            ValidateNotDisposed();
+
+            SignalingServer.Stop(_handle).
+                ThrowIfFailed("Failed to stop signaling server");
+        }
+
+        #region dispose support
+        internal bool IsDisposed => _disposed;
+        /// <summary>
+        /// Releases all resources used by the current instance.
+        /// </summary>
+        /// <exception cref="ObjectDisposedException">The WebRTCSignalingServer has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize((object)this);
+        }
+
+        /// <summary>
+        /// Releases the unmanaged resources used by the <see cref="WebRTCSignalingServer"/>.
+        /// </summary>
+        /// <param name="disposing">
+        /// true to release both managed and unmanaged resources;
+        /// false to release only unmanaged resources.
+        /// </param>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed || !disposing)
+            {
+                return;
+            }
+
+            if (_handle != null)
+            {
+                SignalingServer.Destroy(_handle);
+                _disposed = true;
+            }
+        }
+
+        private void ValidateNotDisposed()
+        {
+            if (_disposed)
+            {
+                Log.Error(WebRTCLog.Tag, "WebRTCSignalingServer was disposed");
+                throw new ObjectDisposedException(nameof(WebRTCSignalingServer));
+            }
+        }
+        #endregion dispose support
+    }
+
+
+    /// <summary>
+    /// Provides the ability to control WebRTCSignalingClient.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class WebRTCSignalingClient : IDisposable
+    {
+        private readonly IntPtr _handle;
+        private bool _isConnected;
+        private SignalingClient.SignalingMessageCallback _signalingMessageCallback;
+        private bool _disposed;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebRTCSignalingClient"/> class.
+        /// </summary>
+        /// <param name="serverIp">The server IP.</param>
+        /// <param name="port">The server port.</param>
+        /// <seealso cref="GetID"/>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public WebRTCSignalingClient(string serverIp, int port)
+        {
+            IntPtr zero = IntPtr.Zero;
+
+            ValidationUtil.ValidateIsNullOrEmpty(serverIp, nameof(serverIp));
+
+            _signalingMessageCallback = (type, message, _) =>
+            {
+                Log.Info(WebRTCLog.Tag, $"type:{type}, message:{message}");
+
+                if (type == SignalingMessageType.Connected)
+                {
+                    _isConnected = true;
+                }
+
+                SignalingMessage?.Invoke(this, new WebRTCSignalingEventArgs(type, message));
+            };
+
+            SignalingClient.Connect(serverIp, port, _signalingMessageCallback, zero, out _handle).
+                ThrowIfFailed("Failed to connect to server");
+        }
+
+        /// <summary>
+        /// Occurs when a message to be handled is sent from the remote peer or the signaling server.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<WebRTCSignalingEventArgs> SignalingMessage;
+
+        /// <summary>
+        /// Gets the state whether signaling client is connected to remote peer or not.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool IsConnected => _isConnected;
+
+        /// <summary>
+        /// Gets the signaling client ID.
+        /// </summary>
+        /// <remarks>
+        /// This method must be called after <see cref="SignalingMessage"/> event is occurred with <see cref="SignalingMessageType.Connected"/>.
+        /// </remarks>
+        /// <returns>The signaling client ID.</returns>
+        /// <exception cref="ObjectDisposedException">The WebRTCSignalingClient has already been disposed.</exception>
+        /// <exception cref="InvalidOperationException">The signaling client is not connected yet.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int GetID()
+        {
+            ValidateNotDisposed();
+
+            if (!IsConnected)
+            {
+                throw new InvalidOperationException("Client is not connected to server yet.");
+            }
+
+            SignalingClient.GetID(_handle, out int id).
+                ThrowIfFailed("Failed to get signaling client ID");
+
+            return id;
+        }
+
+        /// <summary>
+        /// Requests session with peer ID.
+        /// </summary>
+        /// <param name="peerId">The ID of remote peer.</param>
+        /// <exception cref="ObjectDisposedException">The WebRTCSignalingClient has already been disposed.</exception>
+        /// <see cref="SignalingMessageType.SessionEstablished"/>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void RequestSession(int peerId)
+        {
+            ValidateNotDisposed();
+
+            SignalingClient.RequestSession(_handle, peerId).
+                ThrowIfFailed("Failed to request session to peer");
+        }
+
+        /// <summary>
+        /// Sends the signaling message to remote peer.
+        /// </summary>
+        /// <param name="message"></param>
+        /// <exception cref="ObjectDisposedException">The WebRTCSignalingClient has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SendMessage(string message)
+        {
+            ValidateNotDisposed();
+
+            SignalingClient.SendMessage(_handle, message).
+                ThrowIfFailed("Failed to send message to peer");
+        }
+
+        #region dispose support
+        internal bool IsDisposed => _disposed;
+        /// <summary>
+        /// Releases all resources used by the current instance.
+        /// </summary>
+        /// <exception cref="ObjectDisposedException">The WebRTCSignalingClient has already been disposed.</exception>
+        /// <since_tizen> 9 </since_tizen>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize((object)this);
+        }
+
+        /// <summary>
+        /// Releases the unmanaged resources used by the <see cref="WebRTCSignalingClient"/>.
+        /// </summary>
+        /// <param name="disposing">
+        /// true to release both managed and unmanaged resources;
+        /// false to release only unmanaged resources.
+        /// </param>
+        /// <since_tizen> 9 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed || !disposing)
+            {
+                return;
+            }
+
+            if (_handle != null)
+            {
+                SignalingClient.Disconnect(_handle);
+
+                _isConnected = false;
+                _disposed = true;
+            }
+        }
+
+        private void ValidateNotDisposed()
+        {
+            if (_disposed)
+            {
+                Log.Error(WebRTCLog.Tag, "WebRTCSignalingClient was disposed");
+                throw new ObjectDisposedException(nameof(WebRTCSignalingClient));
+            }
+        }
+        #endregion dispose support
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCSignalingStateChangedEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCSignalingStateChangedEventArgs.cs
new file mode 100755 (executable)
index 0000000..3d0a21a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.SignalingStateChanged"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCSignalingStateChangedEventArgs : EventArgs
+    {
+        internal WebRTCSignalingStateChangedEventArgs(WebRTCSignalingState state)
+        {
+            State = state;
+        }
+
+        /// <summary>
+        /// The signaling state.
+        /// </summary>
+        /// <value>The signaling state</value>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCSignalingState State { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"State={State}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCStateChangedEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCStateChangedEventArgs.cs
new file mode 100755 (executable)
index 0000000..c226751
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.StateChanged"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCStateChangedEventArgs : EventArgs
+    {
+        internal WebRTCStateChangedEventArgs(WebRTCState previous, WebRTCState current)
+        {
+            Previous = previous;
+            Current = current;
+        }
+
+        /// <summary>
+        /// The previous state.
+        /// </summary>
+        /// <value>The previous WebRTC state</value>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCState Previous { get; }
+
+        /// <summary>
+        /// The current state.
+        /// </summary>
+        /// <value>The current WebRTC state</value>
+        /// <since_tizen> 9 </since_tizen>
+        public WebRTCState Current { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"Previous state={Previous}, Current state={Current}";
+    }
+}
diff --git a/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCTrackAddedEventArgs.cs b/src/Tizen.Multimedia.Remoting/WebRTC/WebRTCTrackAddedEventArgs.cs
new file mode 100755 (executable)
index 0000000..e95c70e
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Multimedia.Remoting
+{
+    /// <summary>
+    /// Provides data for the <see cref="WebRTC.TrackAdded"/> event.
+    /// </summary>
+    /// <since_tizen> 9 </since_tizen>
+    public class WebRTCTrackAddedEventArgs : EventArgs
+    {
+        internal WebRTCTrackAddedEventArgs(MediaStreamTrack track)
+        {
+            MediaStreamTrack = track;
+        }
+
+        /// <summary>
+        /// Gets the media type.
+        /// </summary>
+        /// <value>The media type.</value>
+        /// <since_tizen> 9 </since_tizen>
+        public MediaStreamTrack MediaStreamTrack { get; }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns>A string that represents the current object.</returns>
+        /// <since_tizen> 9 </since_tizen>
+        public override string ToString() => $"MediaType={MediaStreamTrack.Type}";
+    }
+}