[ACR-1590] Add new API for set param RTP transceiver direction 25/239325/22 accepted/tizen/unified/20200820.034730 submit/tizen/20200819.050443
authorHyunil <hyunil46.park@samsung.com>
Fri, 24 Jul 2020 03:06:21 +0000 (12:06 +0900)
committerHyunil <hyunil46.park@samsung.com>
Thu, 13 Aug 2020 01:28:22 +0000 (10:28 +0900)
- MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO
  MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO.
- It can be "sendrecv" or "sendonly" or "recvonly" or "inactive".

[Version] 0.1.110
[Issue Type] New feature

Change-Id: I5bcef12539338c2a46dc7afb74f2fe42cb2d4a15
Signed-off-by: Hyunil <hyunil46.park@samsung.com>
include/media_streamer.h
include/media_streamer_gst_webrtc.h
include/media_streamer_node.h
include/media_streamer_priv.h
include/media_streamer_util.h
packaging/capi-media-streamer.spec
src/media_streamer.c
src/media_streamer_gst_webrtc.c
src/media_streamer_node.c

index 8bbcc66..aeef5d4 100644 (file)
@@ -329,6 +329,40 @@ typedef enum {
 #define MEDIA_STREAMER_PARAM_WEBRTC_ADD_ICE_CANDIDATE "webrtc-add-ice-candidate"
 
 /**
+ * @brief Definition for WebRTC RTP transceiver direction for audio.
+ * @details WebRTC Transceiver direction to send audio RTP.\n
+ *          It can be "sendrecv" or "sendonly" or "recvonly" or "inactive".
+ * @since_tizen 6.0
+ * @remarks "sendrecv": local peer will offer to send RTP, and send RTP if remote peer accepts,
+ *                      and will offer receive RTP, and will receive RTP if remote peer accepts.\n
+ *          "sendonly": local peer will offer to send RTP and will not suggest to receive RTP,
+ *                      and will not offer to send RTP and will not receive RTP if remote peer accepts.\n
+ *          "recvonly": local peer will not offer to send RTP, and will not send RTP,
+ *                      and will offer to receive RTP, and will receive RTP if remote peer accepts.\n
+ *          "inactive": local peer will not offer to send RTP, and will not send RTP,
+ *                      and will not offer to receive RTP, and will not receive RTP.
+ * @see media_streamer_node_get_params()
+ */
+#define MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO "webrtc-rtp-transceiver-direction-for-audio"
+
+/**
+ * @brief Definition for WebRTC RTP transceiver direction for video.
+ * @details WebRTC Transceiver direction to send video RTP.\n
+ *          It can be "sendrecv" or "sendonly" or "recvonly" or "inactive".
+ * @since_tizen 6.0
+ * @remarks "sendrecv": local peer will offer to send RTP, and send RTP if remote peer accepts,
+ *                      and will offer receive RTP, and will receive RTP if remote peer accepts.\n
+ *          "sendonly": local peer will offer to send RTP and will not suggest to receive RTP,
+ *                      and will not offer to send RTP and will not receive RTP if remote peer accepts.\n
+ *          "recvonly": local peer will not offer to send RTP, and will not send RTP,
+ *                      and will offer to receive RTP, and will receive RTP if remote peer accepts.\n
+ *          "inactive": local peer will not offer to send RTP, and will not send RTP,
+ *                      and will not offer to receive RTP, and will not receive RTP.
+ * @see media_streamer_node_get_params()
+ */
+#define MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO "webrtc-rtp-transceiver-direction-for-video"
+
+/**
  * @brief Definition for audio device name parameter of source or sink node.
  * @details ALSA device, as defined in an asound configuration file.\n
  *          ex) "hw:0,0", "hw:0,1"\n
index e20e676..ea71228 100644 (file)
@@ -54,6 +54,14 @@ int ms_webrtcbin_add_ice_candidate(media_streamer_node_s *webrtc_node, const cha
 
 int ms_webrtcbin_set_stun_server(media_streamer_node_s *webrtc_node, const char *stun_server_url);
 
+void ms_webrtc_set_rtp_transceiver_direction_with_index(GstElement *webrtcbin, gint transceiver_idx, GstWebRTCRTPTransceiverDirection direction);
+
+void ms_webrtc_set_rtp_transceiver_direction_with_caps(GstElement *webrtcbin, const gchar *caps_str, GstWebRTCRTPTransceiverDirection direction);
+
+gboolean ms_webrtc_is_valid_transceiver_direction(gboolean is_linked, GstWebRTCRTPTransceiverDirection direction);
+
+int ms_webrtc_node_get_rtp_transceiver_direction(media_streamer_node_s *webrtc_node, const gchar *param_name, GstWebRTCRTPTransceiverDirection *direction);
+
 #ifdef __cplusplus
 }
 #endif
index 7ed25f2..274fea1 100644 (file)
@@ -18,7 +18,6 @@
 #define __TIZEN_MEDIA_STREAMER_NODE_H__
 
 #include "media_streamer_priv.h"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -126,7 +125,7 @@ gboolean ms_node_dpm_policy_check_iter(const GValue *item, GValue *ret, gpointer
 void ms_node_remove_from_table_cb(void *data);
 GstElement *ms_webrtc_node_get_webrtcbin(media_streamer_node_s *webrtc_node);
 int ms_webrtc_node_is_offerer(media_streamer_node_s *node, gboolean *is_offerer);
-
+int ms_webrtc_node_check_link_condition(media_streamer_node_s *node, const char *sink_pad_name);
 #ifdef __cplusplus
 }
 #endif
index 0e843b3..8a1bcf7 100644 (file)
@@ -75,6 +75,8 @@ typedef enum {
        PROP_NAME_WEBRTC_STUN_SERVER,
        PROP_NAME_WEBRTC_REMOTE_SESSION_DESCRIPTION,
        PROP_NAME_WEBRTC_ADD_ICE_CANDIDATE,
+       PROP_NAME_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO,
+       PROP_NAME_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO,
        PROP_NAME_AUDIO_DEVICE,
        PROP_NAME_SYNC,
        PROP_NAME_ROTATE,
index 4857baa..acdc5f8 100644 (file)
@@ -198,6 +198,14 @@ typedef struct _media_streamer_wl_info_s {
 #define WEBRTC_PEER_OFFER                   "offer"
 #define DEFAULT_WEBRTC_PEER                 WEBRTC_PEER_ANSWER
 #define DEFAULT_WEBRTC_STUN_SERVER          "stun://stun.l.google.com:19302"
+#define WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV    "sendrecv"
+#define WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY    "sendonly"
+#define WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY    "recvonly"
+#define WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE    "inactive"
+#define WEBRTC_RTP_TRANSCEIVER_DIRECTION_UNKNOWN     "unknown"
+#define DEFAULT_WEBRTC_RTP_TRANSCEIVER_DIRECTION     WEBRTC_RTP_TRANSCEIVER_DIRECTION_UNKNOWN
+#define WEBRTC_DEFAULT_VIDEO_RTP_FORMAT    "application/x-rtp,media=video,encoding-name=VP8,payload=96"
+#define WEBRTC_DEFAULT_AUDIO_RTP_FORMAT    "application/x-rtp,media=audio,encoding-name=OPUS,payload=97"
 
 #define MEDIA_STREAMER_DEFAULT_CAMERA_FORMAT "video/x-raw,format=I420,width=352,height=288"
 #define MEDIA_STREAMER_DEFAULT_AUDIO_RAW_FORMAT "audio/x-raw,channels=1,rate=8000,format=S16LE"
@@ -226,6 +234,15 @@ typedef struct _media_streamer_wl_info_s {
 
 #define MEDIA_STREAMER_DEFAULT_DOT_DIR "/tmp"
 
+
+#define MS_SET_BOOLEAN_PARAM(obj, key, value) \
+       do { \
+               GValue *val = g_malloc0(sizeof(GValue)); \
+               g_value_init(val, G_TYPE_BOOLEAN); \
+               g_value_set_boolean(val, value); \
+               g_object_set_data_full(G_OBJECT(obj), key, (gpointer)val, ms_param_value_destroy); \
+       } while (0)
+
 #define MS_SET_INT_PARAM(obj, key, value) \
        do { \
                GValue *val = g_malloc0(sizeof(GValue)); \
index 822b04d..ffe4879 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-streamer
 Summary:    A Media Streamer API
-Version:    0.1.109
+Version:    0.1.110
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 2f1b04c..523d6f3 100644 (file)
@@ -735,6 +735,13 @@ int media_streamer_node_link(media_streamer_node_h node1, const char *src_pad_na
        ms_retvm_if(ms_src_node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node1 is NULL");
        ms_retvm_if(ms_dest_node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node2 is NULL");
 
+    if (ms_dest_node->type == MEDIA_STREAMER_NODE_TYPE_WEBRTC) {
+               if (ms_webrtc_node_check_link_condition(ms_dest_node, sink_pad_name) != MEDIA_STREAMER_ERROR_NONE) {
+                       ms_error("Can not link [%s]:%s pad to [%s]:%s pad", ms_src_node->name, src_pad_name, ms_dest_node->name, sink_pad_name);
+                       return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+               }
+    }
+
        link_ret = gst_element_link_pads(ms_src_node->gst_element, src_pad_name, ms_dest_node->gst_element, sink_pad_name);
 
        if (!link_ret) {
index 0e34ece..af0b9be 100644 (file)
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #include "media_streamer_util.h"
 #include "media_streamer_priv.h"
 #include "media_streamer_gst.h"
@@ -177,6 +176,107 @@ static void __on_answer_created_cb(GstPromise * promise, gpointer user_data)
        ms_debug_fleave();
 }
 
+void ms_webrtc_set_rtp_transceiver_direction_with_index(GstElement *webrtcbin, gint transceiver_idx, GstWebRTCRTPTransceiverDirection direction)
+{
+       GstWebRTCRTPTransceiver *trans = NULL;
+       GArray *transceivers = NULL;
+       ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
+
+       g_signal_emit_by_name (webrtcbin, "get-transceivers", &transceivers);
+       if (transceivers != NULL && transceivers->len > 0) {
+               trans = g_array_index (transceivers, GstWebRTCRTPTransceiver *, transceiver_idx);
+               trans->direction = direction;
+               g_array_unref (transceivers);
+               ms_info("Set transceiver direction(%d) to transceiver_%d", trans->direction, transceiver_idx);
+       } else {
+               ms_error("Could not set transceiver direction(%d) in transceiver_%d", direction, transceiver_idx);
+       }
+}
+
+void ms_webrtc_set_rtp_transceiver_direction_with_caps(GstElement *webrtcbin, const gchar *caps_str, GstWebRTCRTPTransceiverDirection direction)
+{
+       GstWebRTCRTPTransceiver *trans = NULL;
+       GstCaps *caps = NULL;
+
+       ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
+       caps = gst_caps_from_string (caps_str);
+       g_signal_emit_by_name (webrtcbin, "add-transceiver", direction, caps, &trans);
+       gst_caps_unref (caps);
+       if (trans) {
+               ms_info("Set transceiver direction(%d) with caps(%s)", trans->direction, caps_str);
+               gst_object_unref (trans);
+       } else {
+               ms_error("Could not set transceivers direction(%d) with caps(%s)", direction, caps_str);
+       }
+}
+
+gboolean ms_webrtc_is_valid_transceiver_direction(gboolean is_linked, GstWebRTCRTPTransceiverDirection direction)
+{
+       if (is_linked) {
+               if ((direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) &&
+                       (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY)) {
+                       ms_error("To use recvonly or inactive, don't create and link input stream to WebRTC node");
+                       return FALSE;
+               }
+       } else {
+               if ((direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY) &&
+                       (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)) {
+                       ms_error("To use sendonly or sendrecv, create and link input stream to WebRTC node");
+                       return FALSE;
+               }
+       }
+
+       ms_debug("Checked transceiver direction[%d] validity with link status(%s)",direction, (is_linked)? "TRUE": "FALSE");
+
+       return TRUE;
+}
+
+int ms_webrtc_node_get_rtp_transceiver_direction(media_streamer_node_s *webrtc_node, const gchar *param_name, GstWebRTCRTPTransceiverDirection *direction)
+{
+       GValue *val = NULL;
+       const gchar *direction_str = NULL;
+
+       ms_retvm_if(!webrtc_node || !webrtc_node->gst_element, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "webrtc_node is null");
+       ms_retvm_if(!direction, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "transceiver_direction is null");
+
+       if (strcmp(param_name, MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO) &&
+               strcmp(param_name, MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO)) {
+               ms_error("Invalid param name");
+               return MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
+       }
+
+       if (webrtc_node->type != MEDIA_STREAMER_NODE_TYPE_WEBRTC) {
+               ms_error("Invalid node type");
+               return MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
+       }
+
+       val = (GValue *)g_object_get_data(G_OBJECT(webrtc_node->gst_element), param_name);
+       if (!val) {
+               ms_error("Failed to get [%s] val from [%s]", param_name, GST_ELEMENT_NAME(webrtc_node->gst_element));
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       if (!(direction_str = g_value_get_string(val))) {
+               ms_error("Failed to g_value_get_string()");
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       if (!strcmp(direction_str, WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV))
+               *direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
+       else if (!strcmp(direction_str, WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY))
+               *direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
+       else if (!strcmp(direction_str, WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY))
+               *direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
+       else if (!strcmp(direction_str, WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE))
+               *direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE;
+       else if (!strcmp(direction_str, WEBRTC_RTP_TRANSCEIVER_DIRECTION_UNKNOWN))
+               *direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
+       else
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+
+       return MEDIA_STREAMER_ERROR_NONE;
+}
+
 int ms_webrtcbin_set_remote_session_description(media_streamer_node_s *webrtc_node, const char *sdp_msg)
 {
        GstSDPMessage *gst_sdp;
@@ -626,6 +726,8 @@ GstElement *ms_webrtc_element_create(void)
        MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_STUN_SERVER, DEFAULT_WEBRTC_STUN_SERVER);
        MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_REMOTE_SESSION_DESCRIPTION, NULL);
        MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_ADD_ICE_CANDIDATE, NULL);
+       MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO, DEFAULT_WEBRTC_RTP_TRANSCEIVER_DIRECTION);
+       MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO, DEFAULT_WEBRTC_RTP_TRANSCEIVER_DIRECTION);
 
        if (!(webrtcbin = ms_element_create("webrtcbin", NULL))) {
                ms_error("Failed to create webrtcbin element");
index ca938d9..b3203a8 100644 (file)
@@ -153,6 +153,18 @@ static param_s param_table[] = {
                PARAM_DATA_TYPE_STRING
        },
        {
+               MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO,
+               "webrtc-rtp-transceiver-direction-for-audio",
+               PROP_NAME_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO,
+               PARAM_DATA_TYPE_STRING
+       },
+       {
+               MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO,
+               "webrtc-rtp-transceiver-direction-for-video",
+               PROP_NAME_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO,
+               PARAM_DATA_TYPE_STRING
+       },
+       {
                MEDIA_STREAMER_PARAM_AUDIO_DEVICE,
                "audio_device",
                PROP_NAME_AUDIO_DEVICE,
@@ -454,6 +466,57 @@ static int __ms_rtp_node_set_property(media_streamer_node_s *node, param_s *para
        return ret;
 }
 
+static int __ms_webrtc_node_check_transceiver_direction_param_value(media_streamer_node_s *node, param_s *param, const gchar *param_value)
+{
+       GstWebRTCRTPTransceiverDirection direction;
+       GstGhostPad *ghost_pad = NULL;
+       const gchar *pad_name;
+       gboolean is_linked = FALSE;
+
+       ms_retvm_if(!node, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is NULL");
+       ms_retvm_if(!node->gst_element, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "gst_element is NULL");
+       ms_retvm_if(node->type != MEDIA_STREAMER_NODE_TYPE_WEBRTC, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Invalid node type");
+       ms_retvm_if(!param, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "param is NULL");
+       ms_retvm_if(!param_value, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "param_value is NULL");
+
+       if (param->prop_enum == PROP_NAME_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO)
+               pad_name = MS_PAD_AUDIO_IN;
+       else if (param->prop_enum == PROP_NAME_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO)
+               pad_name = MS_PAD_VIDEO_IN;
+       else
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+
+       if (!(ghost_pad = (GstGhostPad *)gst_element_get_static_pad(node->gst_element, pad_name))) {
+               ms_error("Failed to get ghost pad for webrtc_container");
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       is_linked = gst_pad_is_linked((GstPad *)ghost_pad);
+       MS_SAFE_UNREF(ghost_pad);
+
+       ms_debug("[%s] %s", pad_name, is_linked ? "is linked" : "isn't linked");
+
+       if (!strcmp(param_value, WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV)) {
+               direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
+       } else if (!strcmp(param_value, WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY)) {
+               direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
+       } else if (!strcmp(param_value, WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)) {
+               direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
+       } else if (!strcmp(param_value, WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)) {
+               direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE;
+       } else {
+               ms_error("failed to set property, param value should be sendrecv or sendonly or recvonly or inactive");
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       if (!ms_webrtc_is_valid_transceiver_direction(is_linked, direction)) {
+               ms_error("%s is not valid transceiver direction", param_value);
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       return MEDIA_STREAMER_ERROR_NONE;
+}
+
 static gboolean __ms_webrtc_node_has_property(media_streamer_node_s *node, const char *param_name)
 {
        GValue *val = NULL;
@@ -526,6 +589,17 @@ static int __ms_webrtc_node_set_property(media_streamer_node_s *node, param_s *p
                }
                break;
 
+       case PROP_NAME_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO:
+       case PROP_NAME_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO:
+               ret  = __ms_webrtc_node_check_transceiver_direction_param_value(node, param, param_value);
+               if (ret != MEDIA_STREAMER_ERROR_NONE) {
+                       ms_error("failed to set transceiver direction: %s", param_value);
+                       return ret;
+               }
+
+               ret = __ms_param_value_set(val, param->data_type, param_value);
+               break;
+
        default:
                ms_error("failed to set property, undefined param name[%s]", param->param_name);
                return MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
@@ -1816,6 +1890,72 @@ end:
        return ret;
 }
 
+int ms_webrtc_node_check_link_condition(media_streamer_node_s *node, const char *sink_pad_name)
+{
+       GValue *value = NULL;
+       const gchar *key = NULL;
+       const gchar *direction = NULL;
+
+       ms_retvm_if(!node, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is NULL");
+       ms_retvm_if(!node->gst_element, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "gst_element is NULL");
+       ms_retvm_if(!sink_pad_name, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "sink_pad_name is NULL");
+       ms_retvm_if(node->type != MEDIA_STREAMER_NODE_TYPE_WEBRTC, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Node type is not webrtc");
+
+       if (!strcmp(sink_pad_name, MEDIA_STREAMER_NODE_PAD_AUDIO_SINK)) {
+               key = MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO;
+       } else if (!strcmp(sink_pad_name, MEDIA_STREAMER_NODE_PAD_VIDEO_SINK)) {
+               key = MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO;
+       } else {
+               ms_error("sink_pand_name[%s] is wrong", sink_pad_name);
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       value = (GValue *)g_object_get_data(G_OBJECT(node->gst_element), key);
+       if (!value) {
+               ms_error("failed to get [%s] val from [%s]", key, GST_ELEMENT_NAME(node->gst_element));
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       if (!(direction = g_value_get_string(value))) {
+               ms_error("Failed to g_value_get_string()");
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       if (!strcmp(direction, WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY) &&
+               !strcmp(direction, WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)) {
+               ms_error("Could not link to [%s] in %s", sink_pad_name, direction);
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       return MEDIA_STREAMER_ERROR_NONE;
+}
+static int __ms_webrtc_node_update_transceiver_direction_param_value(media_streamer_node_s *node, const gchar *param_name, const gchar *direction)
+{
+       int ret = MEDIA_STREAMER_ERROR_NONE;
+       GValue *value = NULL;
+
+       ms_retvm_if(!node, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is NULL");
+       ms_retvm_if(!node->gst_element, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "gst_element is NULL");
+       ms_retvm_if(node->type != MEDIA_STREAMER_NODE_TYPE_WEBRTC, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Node type is not webrtc");
+       ms_retvm_if(!direction, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "direction is NULL");
+
+       ms_debug("direction is [%s]", direction);
+
+       value = (GValue *)g_object_get_data(G_OBJECT(node->gst_element), param_name);
+       if (!value) {
+               ms_error("failed to get [%s] val from [%s]", param_name, GST_ELEMENT_NAME(node->gst_element));
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       ret = __ms_param_value_set(value, PARAM_DATA_TYPE_STRING, direction);
+       if (ret != MEDIA_STREAMER_ERROR_NONE) {
+               return ret;
+               ms_info("update transceiver unknown -> sendrecv");
+       }
+
+       return ret;
+}
+
 GstElement * ms_webrtc_node_get_webrtcbin(media_streamer_node_s *webrtc_node)
 {
        GstElement *webrtcbin = NULL;
@@ -1836,6 +1976,80 @@ GstElement * ms_webrtc_node_get_webrtcbin(media_streamer_node_s *webrtc_node)
        return webrtcbin;
 }
 
+static int __ms_webrtc_node_set_rtp_transceiver_direction(media_streamer_node_s *webrtc_node)
+{
+       int ret = MEDIA_STREAMER_ERROR_NONE;
+       GstElement *webrtcbin = NULL;
+       GstElement *video_filter = NULL;
+       GstElement *audio_filter = NULL;
+       gint transceiver_idx = 0;
+       gboolean is_linked_video = FALSE;
+       gboolean is_linked_audio = FALSE;
+       GstWebRTCRTPTransceiverDirection audio_direction;
+       GstWebRTCRTPTransceiverDirection video_direction;
+       const gchar *audio_param_name = MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_AUDIO;
+       const gchar *video_param_name = MEDIA_STREAMER_PARAM_WEBRTC_RTP_TRANSCEIVER_DIRECTION_FOR_VIDEO;
+
+       ms_retvm_if(webrtc_node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "webrtc_node is NULL");
+       ms_retvm_if(webrtc_node->gst_element == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "webrtc_container is NULL");
+       ms_debug_fenter();
+
+       if (!(webrtcbin = ms_webrtc_node_get_webrtcbin(webrtc_node)))
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+
+       if (ms_webrtc_node_get_rtp_transceiver_direction(webrtc_node, audio_param_name, &audio_direction)) {
+               ms_error("Failed to get WebRTC audio RTP transceiver direction");
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+       if (ms_webrtc_node_get_rtp_transceiver_direction(webrtc_node, video_param_name, &video_direction)) {
+               ms_error("Failed to get WebRTC video RTP transceiver direction");
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
+       if ((audio_filter = ms_find_element_in_bin_by_name(webrtc_node->gst_element, "audio_capsfilter")))
+               is_linked_audio = TRUE;
+
+       if ((video_filter = ms_find_element_in_bin_by_name(webrtc_node->gst_element, "video_capsfilter")))
+               is_linked_video = TRUE;
+
+       /* GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE indicates that transceiver direction was not set by user,
+          it will be changed to sendrecv. */
+       if (audio_direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
+               if (is_linked_audio) {
+                       if (__ms_webrtc_node_update_transceiver_direction_param_value(webrtc_node, audio_param_name, WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV))
+                               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+               }
+       } else {
+               if (!ms_webrtc_is_valid_transceiver_direction(is_linked_audio, audio_direction))
+                       return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+
+               if (is_linked_audio)
+                       ms_webrtc_set_rtp_transceiver_direction_with_index(webrtcbin, transceiver_idx++, audio_direction);
+               else
+                       ms_webrtc_set_rtp_transceiver_direction_with_caps(webrtcbin, WEBRTC_DEFAULT_AUDIO_RTP_FORMAT, audio_direction);
+       }
+
+       if (video_direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
+               if (is_linked_video) {
+                       if (__ms_webrtc_node_update_transceiver_direction_param_value(webrtc_node, video_param_name, WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV))
+                               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+               }
+       } else {
+               if (!ms_webrtc_is_valid_transceiver_direction(is_linked_video, video_direction))
+                       return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+
+               if (is_linked_video)
+                       ms_webrtc_set_rtp_transceiver_direction_with_index(webrtcbin, transceiver_idx, video_direction);
+               else
+                       ms_webrtc_set_rtp_transceiver_direction_with_caps(webrtcbin, WEBRTC_DEFAULT_VIDEO_RTP_FORMAT, video_direction);
+       }
+
+       ms_debug_fleave();
+
+       return ret;
+}
+
+
 int ms_webrtc_node_prepare(media_streamer_s *ms_streamer, media_streamer_node_s *node)
 {
        int ret = MEDIA_STREAMER_ERROR_NONE;
@@ -1877,6 +2091,13 @@ int ms_webrtc_node_prepare(media_streamer_s *ms_streamer, media_streamer_node_s
        if ((ret = __ms_webrtc_prepare_ghost_sink_pad(node->gst_element, webrtcbin, _WEBRTC_VIDEO_CAPSFILTER)))
                return ret;
 
+       /* The transceiver is added when the ghost pad is linked to webrtcbin. Therefore,
+          the transceiver direction must be update after __ms_webrtc_prepare_ghost_sink_pad(). */
+       if (__ms_webrtc_node_set_rtp_transceiver_direction(node) != MEDIA_STREAMER_ERROR_NONE) {
+               ms_error("Failed to set WebRTC RTP transceiver direction");
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+
        if (ms_element_set_state(webrtcbin, GST_STATE_READY)) {
                ms_error("Failed to set state to READY");
                return MEDIA_STREAMER_ERROR_INVALID_OPERATION;