Add API set to get/set transceiver direction 19/244719/7
authorSangchul Lee <sc11.lee@samsung.com>
Wed, 23 Sep 2020 11:13:50 +0000 (20:13 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Thu, 8 Oct 2020 07:02:12 +0000 (16:02 +0900)
Enums are added as below.
 - WEBRTC_MEDIA_TYPE_AUDIO
 - WEBRTC_MEDIA_TYPE_VIDEO
 - WEBRTC_TRANSCEIVER_DIRECTION_SENDONLY
 - WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY
 - WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV

Functions are added as below.
 - webrtc_get_transceiver_direction()
 - webrtc_set_transceiver_direction()

Test cases for these functions are added to webrtc_test.

[Version] 0.1.31
[Issue Type] API

Change-Id: I6753b7480a6b363f262cf9edbcdf08c9cb20f24c
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
include/webrtc.h
include/webrtc_private.h
packaging/capi-media-webrtc.spec
src/webrtc.c
src/webrtc_private.c
test/webrtc_test.c

index 0c3cdae1288898182cd13a0a25917861575714ea..1ebb5d86134ae79a57db2fad40229cb18bd7dd38 100644 (file)
@@ -83,6 +83,25 @@ typedef enum {
        WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST,   /**<  Video test */
 } webrtc_media_source_type_e;
 
+/**
+ * @brief Enumeration for WebRTC media type.
+ * @since_tizen 6.5
+ */
+typedef enum {
+       WEBRTC_MEDIA_TYPE_AUDIO,
+       WEBRTC_MEDIA_TYPE_VIDEO,
+} webrtc_media_type_e;
+
+/**
+ * @brief Enumeration for WebRTC transceiver direction type.
+ * @since_tizen 6.5
+ */
+typedef enum {
+       WEBRTC_TRANSCEIVER_DIRECTION_SENDONLY,
+       WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY,
+       WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV,
+} webrtc_transceiver_direction_e;
+
 /**
  * @brief Called when an error occurs.
  * @details The following error codes can be received:\n
@@ -299,12 +318,48 @@ int webrtc_add_media_source(webrtc_h webrtc, webrtc_media_source_type_e type, un
  * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter
  * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation
  * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state
+ * @pre Add media source to @a webrtc to get @a source_id by calling webrtc_add_media_source().
  * @pre @a webrtc state must be set to #WEBRTC_STATE_IDLE.
- * @pre Add media source to @a webrtc by calling webrtc_add_media_source().
  * @see webrtc_add_media_source()
  */
 int webrtc_remove_media_source(webrtc_h webrtc, unsigned int source_id);
 
+/**
+ * @brief Gets the transceiver direction of the media source with specified media type.
+ * @since_tizen 6.5
+ * @param[in] webrtc      WebRTC handle
+ * @param[in] source_id   The media source id
+ * @param[in] media_type  The media type
+ * @param[out] direction  Current transceiver direction
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #WEBRTC_ERROR_NONE    Successful
+ * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation
+ * @pre Add media source to @a webrtc to get @a source_id by calling webrtc_add_media_source().
+ * @see webrtc_set_transceiver_direction()
+ */
+int webrtc_get_transceiver_direction(webrtc_h webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e *direction);
+
+/**
+ * @brief Sets the transceiver direction to the media source with specified media type.
+ * @since_tizen 6.5
+ * @param[in] webrtc      WebRTC handle
+ * @param[in] source_id   The media source id
+ * @param[in] media_type  The media type
+ * @param[in] direction   The transceiver direction to set
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #WEBRTC_ERROR_NONE    Successful
+ * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state
+ * @pre Add media source to @a webrtc to get @a source_id by calling webrtc_add_media_source().
+ * @pre @a webrtc state must be set to #WEBRTC_STATE_IDLE.
+ * @see webrtc_get_transceiver_direction()
+ */
+int webrtc_set_transceiver_direction(webrtc_h webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction);
+
 /**
  * @brief Sets a STUN server URL.
  * @since_tizen 6.5
index 0f7f4a937d5601c4babfce29fdaf9c4a033ec5c0..3205b45220f3d87c08d5ddd7ddbe7ffb48da5512 100644 (file)
@@ -183,6 +183,8 @@ void _gst_destroy_pipeline(webrtc_s *webrtc);
 int _gst_pipeline_set_state(webrtc_s *webrtc, GstState state);
 int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigned int *source_id);
 int _remove_media_source(webrtc_s *webrtc, unsigned int source_id);
+int _get_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e *direction);
+int _set_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction);
 
 int _webrtcbin_create_offer(webrtc_s *webrtc, char **offer);
 int _webrtcbin_create_answer(webrtc_s *webrtc, char **answer);
index e078c181842a711d318ded731d63cd8bd484a055..00629e4b595d8e56f81e276401ec01dedadd0bc9 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.1.30
+Version:    0.1.31
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 8a78d5bd23aa52b717f4eb7920eecc184f02b061..b80d39ef7df7b79146b19940d4f7fe5eec9bfe38 100644 (file)
@@ -235,7 +235,7 @@ int webrtc_add_media_source(webrtc_h webrtc, webrtc_media_source_type_e type, un
        g_mutex_unlock(&_webrtc->mutex);
 
        if (ret == WEBRTC_ERROR_NONE)
-               LOG_INFO("source_id[%d]", *source_id);
+               LOG_INFO("source_id[%u]", *source_id);
 
        return ret;
 }
@@ -251,7 +251,7 @@ int webrtc_remove_media_source(webrtc_h webrtc, unsigned int source_id)
 
        RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_IDLE, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be IDLE");
 
-       LOG_INFO("source_id[%d]", source_id);
+       LOG_INFO("source_id[%u]", source_id);
 
        ret = _remove_media_source(webrtc, source_id);
 
@@ -260,6 +260,46 @@ int webrtc_remove_media_source(webrtc_h webrtc, unsigned int source_id)
        return ret;
 }
 
+int webrtc_get_transceiver_direction(webrtc_h webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e *direction)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       webrtc_s *_webrtc = (webrtc_s*)webrtc;
+
+       RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(direction == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "direction is NULL");
+
+       g_mutex_lock(&_webrtc->mutex);
+
+       ret = _get_transceiver_direction(webrtc, source_id, media_type, direction);
+
+       g_mutex_unlock(&_webrtc->mutex);
+
+       if (ret == WEBRTC_ERROR_NONE)
+               LOG_INFO("source_id[%u], media_type[%d], direction[%d]", source_id, media_type, *direction);
+
+       return ret;
+}
+
+int webrtc_set_transceiver_direction(webrtc_h webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       webrtc_s *_webrtc = (webrtc_s*)webrtc;
+
+       RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+
+       g_mutex_lock(&_webrtc->mutex);
+
+       RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_IDLE, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be IDLE");
+
+       ret = _set_transceiver_direction(webrtc, source_id, media_type, direction);
+
+       g_mutex_unlock(&_webrtc->mutex);
+
+       LOG_INFO("source_id[%u], media_type[%d], direction[%d]", source_id, media_type, direction);
+
+       return ret;
+}
+
 int webrtc_set_stun_server(webrtc_h webrtc, const char *stun_server)
 {
        webrtc_s *_webrtc = (webrtc_s*)webrtc;
index 0c1b427276f8a90ee0125eca2496b4c0f883dc39..eb0efbb021b5f4c25a1d5911cd8bb58d793c6278 100644 (file)
@@ -125,6 +125,18 @@ static const char* __state_str[] = {
        "PLAYING",
 };
 
+static const char* __direction_str[] = {
+       "SENDONLY",
+       "RECVONLY",
+       "SENDRECV",
+};
+
+static GstWebRTCRTPTransceiverDirection __direction_gst[] = {
+       GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY,
+       GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY,
+       GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
+};
+
 static void __generate_dot(webrtc_s *webrtc, const gchar *name)
 {
        gchar *dot_name;
@@ -572,6 +584,8 @@ static void __webrtcbin_on_ice_candidate_cb(GstElement *webrtcbin, guint mlinein
        RET_IF(webrtc == NULL, "webrtc is NULL");
        RET_IF(webrtc->ice_candidate_cb.callback == NULL, "ice_candidate_cb is NULL");
 
+       LOG_DEBUG("mlineindex[%u], candidate[%s]", mlineindex, candidate);
+
        _candidate = __make_ice_candidate_message(mlineindex, candidate);
        if (!_candidate)
                return;
@@ -1700,6 +1714,135 @@ int _remove_media_source(webrtc_s *webrtc, unsigned int source_id)
        return ret;
 }
 
+static gboolean __check_id_equal_cb(gpointer key, gpointer value, gpointer user_data)
+{
+       webrtc_gst_slot_s *slot = value;
+
+       if (slot->id == GPOINTER_TO_UINT(user_data)) {
+               LOG_DEBUG("found slot[%s] of id[%u]", GST_ELEMENT_NAME(slot->bin), slot->id);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static webrtc_gst_slot_s* __get_slot_by_id(GHashTable *slots, unsigned int id)
+{
+       webrtc_gst_slot_s *slot;
+
+       RET_VAL_IF(slots == NULL, NULL, "slots is NULL");
+
+       slot = g_hash_table_find(slots, __check_id_equal_cb, GUINT_TO_POINTER(id));
+       if (!slot)
+               LOG_ERROR("could not find any slot of id[%u]", id);
+
+       return slot;
+}
+
+static int __convert_direction(GstWebRTCRTPTransceiverDirection gst_direction, webrtc_transceiver_direction_e *direction)
+{
+       RET_VAL_IF(direction == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "direction is NULL");
+
+       if (gst_direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE ||
+               gst_direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
+               LOG_ERROR("not expected gst direction[%d]", gst_direction);
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       if (gst_direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY)
+               *direction = WEBRTC_TRANSCEIVER_DIRECTION_SENDONLY;
+       else if (gst_direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
+               *direction = WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY;
+       else
+               *direction = WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV;
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int _get_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e *direction)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       webrtc_gst_slot_s *source;
+       GstWebRTCRTPTransceiver *trans;
+       GArray *transceivers;
+       int mline;
+       int i;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(direction == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "direction is NULL");
+       RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL");
+       RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
+       RET_VAL_IF((source = __get_slot_by_id(webrtc->gst.source_slots, source_id)) == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+
+       if (media_type == WEBRTC_MEDIA_TYPE_AUDIO && source->media_types & MEDIA_TYPE_AUDIO) {
+               mline = source->mlines[MLINES_IDX_AUDIO];
+
+       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO && source->media_types & MEDIA_TYPE_VIDEO) {
+               mline = source->mlines[MLINES_IDX_VIDEO];
+
+       } else {
+               LOG_ERROR("invalid media_type[%d] for source[media_types:0x%x, id:%u]", media_type, source->media_types, source_id);
+               return WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       g_signal_emit_by_name(webrtc->gst.webrtcbin, "get-transceivers", &transceivers);
+       for (i = 0; i < transceivers->len; i++) {
+               trans = g_array_index(transceivers, GstWebRTCRTPTransceiver *, i);
+               if (trans->mline == mline) {
+                       ret = __convert_direction(trans->direction, direction);
+                       if (ret != WEBRTC_ERROR_NONE)
+                               return ret;
+                       LOG_DEBUG("found transceiver[%p, index:%d, mline:%u, mid:%s, direction:%s]",
+                               trans, i, trans->mline, trans->mid, __direction_str[*direction]);
+                       return WEBRTC_ERROR_NONE;
+               }
+       }
+
+       LOG_ERROR("could not find a transceiver with mline[%d]", mline);
+
+       return WEBRTC_ERROR_INVALID_OPERATION;
+}
+
+int _set_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction)
+{
+       webrtc_gst_slot_s *source;
+       GstWebRTCRTPTransceiver *trans;
+       GArray *transceivers;
+       int mline;
+       int i;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(direction > WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV, WEBRTC_ERROR_INVALID_PARAMETER, "invalid direction");
+       RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL");
+       RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
+       RET_VAL_IF((source = __get_slot_by_id(webrtc->gst.source_slots, source_id)) == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+
+       if (media_type == WEBRTC_MEDIA_TYPE_AUDIO && source->media_types & MEDIA_TYPE_AUDIO) {
+               mline = source->mlines[MLINES_IDX_AUDIO];
+
+       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO && source->media_types & MEDIA_TYPE_VIDEO) {
+               mline = source->mlines[MLINES_IDX_VIDEO];
+
+       } else {
+               LOG_ERROR("invalid media_type[%d] for source[media_types:0x%x, id:%u]", media_type, source->media_types, source_id);
+               return WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       g_signal_emit_by_name(webrtc->gst.webrtcbin, "get-transceivers", &transceivers);
+       for (i = 0; i < transceivers->len; i++) {
+               trans = g_array_index(transceivers, GstWebRTCRTPTransceiver *, i);
+               if (trans->mline == mline) {
+                       trans->direction = __direction_gst[direction];
+                       LOG_DEBUG("Set direction to transceiver[%p, index:%d, mline:%u, direction:%s]",
+                               trans, i, trans->mline, __direction_str[direction]);
+                       return WEBRTC_ERROR_NONE;
+               }
+       }
+
+       LOG_ERROR("could not find a transceiver with mline[%d]", mline);
+
+       return WEBRTC_ERROR_INVALID_OPERATION;
+}
+
 void _webrtcbin_on_negotiation_needed(GstElement *webrtcbin, gpointer user_data)
 {
        webrtc_s *webrtc = (webrtc_s *)user_data;
index 9bfa6aa005ab4540b8f228b1e11ac0f0fe8d957a..f5b1f4aad72e69dbe5b57b0138626bb4f918645c 100644 (file)
@@ -37,6 +37,8 @@ enum {
        CURRENT_STATUS_MAINMENU,
        CURRENT_STATUS_ADD_MEDIA_SOURCE,
        CURRENT_STATUS_REMOVE_MEDIA_SOURCE,
+       CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION,
+       CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION,
        CURRENT_STATUS_SET_STUN_SERVER,
        CURRENT_STATUS_SET_LOCAL_DESCRIPTION,
        CURRENT_STATUS_SET_REMOTE_DESCRIPTION,
@@ -66,6 +68,17 @@ static const char* g_webrtc_state_str[] = {
        "PLAYING",
 };
 
+static const char* g_webrtc_media_type_str[] = {
+       "AUDIO",
+       "VIDEO",
+};
+
+static const char* g_webrtc_transceiver_direction_str[] = {
+       "SENDONLY",
+       "RECVONLY",
+       "SENDRECV",
+};
+
 /* for video display */
 static Evas_Object *g_eo;
 static Evas_Object *g_win_id;
@@ -89,6 +102,7 @@ static gchar g_stun_server[MAX_STRING_LEN];
 static gchar g_proxy[MAX_STRING_LEN];
 static int g_server_status = SERVER_STATUS_DISCONNECTED;
 static int g_menu_state = CURRENT_STATUS_MAINMENU;
+static int g_cnt;
 
 static void win_del(void *data, Evas_Object *obj, void *event)
 {
@@ -292,7 +306,7 @@ static void _webrtc_add_media_source(int type)
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_add_media_source(), ret[0x%x]\n", ret);
        else
-               g_print("webrtc_add_media_source() success, source_id[%d]\n", source_id);
+               g_print("webrtc_add_media_source() success, source_id[%u]\n", source_id);
 }
 
 static void _webrtc_remove_media_source(unsigned int source_id)
@@ -301,9 +315,34 @@ static void _webrtc_remove_media_source(unsigned int source_id)
 
        ret = webrtc_remove_media_source(g_webrtc, source_id);
        if (ret != WEBRTC_ERROR_NONE)
-               g_print("failed to webrtc_remove_media_source(), source_id[%d], ret[0x%x]\n", source_id, ret);
+               g_print("failed to webrtc_remove_media_source(), source_id[%u], ret[0x%x]\n", source_id, ret);
+       else
+               g_print("webrtc_remove_media_source() success, source_id[%u]\n", source_id);
+}
+
+static void _webrtc_get_transceiver_direction(unsigned int source_id, webrtc_media_type_e media_type)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       webrtc_transceiver_direction_e direction;
+
+       ret = webrtc_get_transceiver_direction(g_webrtc, source_id, media_type, &direction);
+       if (ret != WEBRTC_ERROR_NONE)
+               g_print("failed to webrtc_get_transceiver_direction(), ret[0x%x]\n", ret);
        else
-               g_print("webrtc_remove_media_source() success, source_id[%d]\n", source_id);
+               g_print("webrtc_get_transceiver_direction() success, source_id[%u], media_type[%s], direction[%s]\n",
+                       source_id, g_webrtc_media_type_str[media_type], g_webrtc_transceiver_direction_str[direction]);
+}
+
+static void _webrtc_set_transceiver_direction(unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction)
+{
+       int ret = WEBRTC_ERROR_NONE;
+
+       ret = webrtc_set_transceiver_direction(g_webrtc, source_id, media_type, direction);
+       if (ret != WEBRTC_ERROR_NONE)
+               g_print("failed to webrtc_set_transceiver_direction(), source_id[%u], ret[0x%x]\n", source_id, ret);
+       else
+               g_print("webrtc_set_transceiver_direction() success, source_id[%u], media_type[%s], direction[%s]\n",
+                       source_id, g_webrtc_media_type_str[media_type], g_webrtc_transceiver_direction_str[direction]);
 }
 
 static int __copy_string_arr(gchar *dest_arr, char *string)
@@ -794,7 +833,13 @@ void _interpret_main_menu(char *cmd)
                }
 
        } else if (len == 2) {
-               if (strncmp(cmd, "se", 2) == 0) {
+               if (strncmp(cmd, "gd", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION;
+
+               } else if (strncmp(cmd, "td", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION;
+
+               } else if (strncmp(cmd, "se", 2) == 0) {
                        _webrtc_set_error_cb();
 
                } else if (strncmp(cmd, "ue", 2) == 0) {
@@ -911,6 +956,8 @@ void display_sub_basic()
        g_print("g. Get state\n");
        g_print("a. Add media source\t");
        g_print("r. Remove media source\n");
+       g_print("gd. Get transceiver direction\t");
+       g_print("td. Set transceiver direction\n");
        g_print("se. Set error callback\t");
        g_print("ue. Unset error callback\n");
        g_print("sc. Set state changed callback\t");
@@ -940,26 +987,50 @@ static void displaymenu()
 {
        if (g_menu_state == CURRENT_STATUS_MAINMENU) {
                display_sub_basic();
+
        } else if (g_menu_state == CURRENT_STATUS_ADD_MEDIA_SOURCE) {
                g_print("*** input media source type.(1:camera, 2:mic, 3:audiotest, 4:videotest)\n");
+
        } else if (g_menu_state == CURRENT_STATUS_REMOVE_MEDIA_SOURCE) {
                g_print("*** input media source id to remove.\n");
+
+       } else if (g_menu_state == CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION) {
+               if (g_cnt == 0)
+                       g_print("*** input source id.\n");
+               else if (g_cnt == 1)
+                       g_print("*** input media type.(1:audio 2:video)\n");
+
+       } else if (g_menu_state == CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION) {
+               if (g_cnt == 0)
+                       g_print("*** input source id.\n");
+               else if (g_cnt == 1)
+                       g_print("*** input media type.(1:audio 2:video)\n");
+               else if (g_cnt == 2)
+                       g_print("*** input transceiver direction.(1:sendonly 2:recvonly, 3:sendrecv)\n");
+
        } else if (g_menu_state == CURRENT_STATUS_SET_STUN_SERVER) {
                g_print("*** input STUN server address.\n");
+
        } else if (g_menu_state == CURRENT_STATUS_SET_LOCAL_DESCRIPTION) {
                g_print("*** input type of local description.(1:offer, 2:answer)\n");
+
        } else if (g_menu_state == CURRENT_STATUS_SETTING_SIGNALING_SERVER) {
                g_print("*** input signaling server URL.\n");
+
        } else if (g_menu_state == CURRENT_STATUS_SETTING_PROXY) {
                g_print("*** input proxy URL.\n");
+
        } else if (g_menu_state == CURRENT_STATUS_REQUEST_SESSION) {
                g_print("*** input remote peer id.\n");
+
        } else if (g_menu_state == CURRENT_STATUS_SEND_LOCAL_DESCRIPTION) {
                g_print("*** input type of local description to send to the sever.(1:offer, 2:answer)\n");
+
        } else {
                g_print("*** unknown status.\n");
                quit_program();
        }
+
        g_print(" >>> ");
 }
 
@@ -1000,6 +1071,51 @@ static void interpret(char *cmd)
                reset_menu_state();
                break;
        }
+       case CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION: {
+               static unsigned int id;
+               static unsigned int media_type;
+               value = atoi(cmd);
+
+               switch (g_cnt) {
+               case 0:
+                       id = value;
+                       g_cnt++;
+                       break;
+               case 1:
+                       media_type = value - 1;
+                       _webrtc_get_transceiver_direction(id, media_type);
+                       id = media_type = 0;
+                       g_cnt = 0;
+                       reset_menu_state();
+                       break;
+               }
+               break;
+       }
+       case CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION: {
+               static unsigned int id;
+               static unsigned int media_type;
+               static unsigned int direction;
+               value = atoi(cmd);
+
+               switch (g_cnt) {
+               case 0:
+                       id = value;
+                       g_cnt++;
+                       break;
+               case 1:
+                       media_type = value - 1;
+                       g_cnt++;
+                       break;
+               case 2:
+                       direction =  value - 1;
+                       _webrtc_set_transceiver_direction(id, media_type, direction);
+                       id = media_type = direction = 0;
+                       g_cnt = 0;
+                       reset_menu_state();
+                       break;
+               }
+               break;
+       }
        case CURRENT_STATUS_SET_STUN_SERVER: {
                _webrtc_set_stun_server(cmd);
                reset_menu_state();