Add API to set/get display mode 65/261465/16
authorSangchul Lee <sc11.lee@samsung.com>
Mon, 19 Jul 2021 11:34:57 +0000 (20:34 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Tue, 10 Aug 2021 07:38:22 +0000 (16:38 +0900)
Enums are added as below.
 - WEBRTC_DISPLAY_MODE_LETTER_BOX
 - WEBRTC_DISPLAY_MODE_ORIGIN_SIZE
 - WEBRTC_DISPLAY_MODE_FULL

Functions are added as below.
 - webrtc_set_display_mode()
 - webrtc_get_display_mode()

[Version] 0.2.69
[Issue Type] API

Change-Id: Ia691e6091fb2059c069c2c7202efcd4fc61cdf85
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_display.c
src/webrtc_sink.c
src/webrtc_source.c
test/webrtc_test.c

index 1fcaeb34a0deed58f540cf6934104bc7bad34e41..ec6b3f819232f2aa1595aee889292a04e1fb4220 100644 (file)
@@ -214,6 +214,16 @@ typedef enum {
        WEBRTC_DISPLAY_TYPE_EVAS,         /**< Evas image object */
 } webrtc_display_type_e;
 
+/**
+ * @brief Enumeration for WebRTC display mode.
+ * @since_tizen 6.5
+ */
+typedef enum {
+       WEBRTC_DISPLAY_MODE_LETTER_BOX,   /**< Letter box */
+       WEBRTC_DISPLAY_MODE_ORIGIN_SIZE,  /**< Origin size */
+       WEBRTC_DISPLAY_MODE_FULL,         /**< Full screen */
+} webrtc_display_mode_e;
+
 /**
  * @}
  */
@@ -1085,9 +1095,46 @@ int webrtc_set_sound_stream_info(webrtc_h webrtc, unsigned int track_id, sound_s
  * @pre webrtc_track_added_cb() must be set by calling webrtc_set_track_added_cb().
  * @see webrtc_set_track_added_cb()
  * @see webrtc_unset_track_added_cb()
+ * @see webrtc_set_display_mode()
+ * @see webrtc_get_display_mode()
  */
 int webrtc_set_display(webrtc_h webrtc, unsigned int track_id, webrtc_display_type_e type, webrtc_display_h display);
 
+/**
+ * @brief Sets the display mode of the video track.
+ * @since_tizen 6.5
+ * @param[in] webrtc      WebRTC handle
+ * @param[in] track_id    The track id
+ * @param[in] mode        The display mode
+ * @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 For remote video track, webrtc_set_display() must be called with @a track_id from webrtc_track_added_cb().\n
+ *      For loopback video track, webrtc_media_source_set_video_loopback() must be called to get @a track_id.
+ * @see webrtc_get_display_mode()
+ */
+int webrtc_set_display_mode(webrtc_h webrtc, unsigned int track_id, webrtc_display_mode_e mode);
+
+/**
+ * @brief Gets the display mode of the video track.
+ * @since_tizen 6.5
+ * @remarks The default value is #WEBRTC_DISPLAY_MODE_LETTER_BOX.
+ * @param[in] webrtc      WebRTC handle
+ * @param[in] track_id    The track id
+ * @param[out] mode       The display mode
+ * @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 For remote video track, webrtc_set_display() must be called with @a track_id from webrtc_track_added_cb().\n
+ *      For loopback video track, webrtc_media_source_set_video_loopback() must be called to get @a track_id.
+ * @see webrtc_set_display_mode()
+ */
+int webrtc_get_display_mode(webrtc_h webrtc, unsigned int track_id, webrtc_display_mode_e *mode);
+
 /**
  * @brief Sets an encoded audio frame callback function to be invoked when each audio frame is ready to be rendered.
  * @since_tizen 6.5
@@ -1204,6 +1251,8 @@ int webrtc_media_source_set_audio_loopback(webrtc_h webrtc, unsigned int source_
  * @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_media_source_set_audio_loopback()
+ * @see webrtc_set_display_mode()
+ * @see webrtc_get_display_mode()
  */
 int webrtc_media_source_set_video_loopback(webrtc_h webrtc, unsigned int source_id, webrtc_display_type_e type, webrtc_display_h display, unsigned int *track_id);
 
index 43fc5efeb75b9887c19d64384ee00eff03e49366..87d5705f1aeb245bcf839f5d573761d596fe9b15 100644 (file)
@@ -231,6 +231,7 @@ do { \
 
 #define WEBRTC_DISPLAY_TYPE_ECORE_WL    2
 #define PAYLOAD_ID_BITS                 32 /* 96 ~ 127 */
+#define TRACK_ID_THRESHOLD_OF_LOOPBACK  100
 
 /* NOTE : GstAutoplugSelectResult is defined in gstplay-enum.h but not exposed
  We are defining our own and will be removed when it actually exposed */
@@ -367,7 +368,9 @@ typedef struct _webrtc_display {
 
        GMutex mutex;
        mm_display_interface_h mm_display;
+       webrtc_display_mode_e mode;
        webrtc_tbm_s *tbm;
+       GstElement *sink_element;
 } webrtc_display_s;
 
 typedef struct _webrtc_gst_s {
@@ -581,6 +584,10 @@ int _add_rendering_sink_bin(webrtc_s *webrtc, GstPad *src_pad);
 int _add_forwarding_sink_bin(webrtc_s *webrtc, GstPad *src_pad, bool is_video);
 int _set_stream_info_to_sink(webrtc_s *webrtc, unsigned int track_id, sound_stream_info_h stream_info);
 int _set_display_to_sink(webrtc_s *webrtc, unsigned int track_id, unsigned int type, void *display);
+int _set_display_mode_to_sink(webrtc_s *webrtc, unsigned int track_id, webrtc_display_mode_e mode);
+int _get_display_mode_from_sink(webrtc_s *webrtc, unsigned int track_id, webrtc_display_mode_e *mode);
+int _set_display_mode_to_loopback(webrtc_s *webrtc, unsigned int track_id, webrtc_display_mode_e mode);
+int _get_display_mode_from_loopback(webrtc_s *webrtc, unsigned int track_id, webrtc_display_mode_e *mode);
 int _set_audio_loopback(webrtc_s *webrtc, unsigned int source_id, sound_stream_info_h stream_info, unsigned int *track_id);
 int _set_video_loopback(webrtc_s *webrtc, unsigned int source_id, unsigned int type, void *display, unsigned int *track_id);
 int _decodebin_autoplug_select_cb(GstElement *decodebin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, gpointer user_data);
@@ -596,6 +603,8 @@ webrtc_display_s *_alloc_display(void);
 void _release_display(webrtc_display_s *display);
 int _apply_display(webrtc_display_s *display);
 void _set_display_type_and_surface(webrtc_display_s *display, webrtc_display_type_e type, void *surface);
+int _set_display_mode(webrtc_display_s *display, webrtc_display_mode_e mode);
+int _get_display_mode(webrtc_display_s *display, webrtc_display_mode_e *mode);
 void _video_stream_decoded_cb(GstElement *object, GstBuffer *buffer, GstPad *pad, gpointer data);
 
 #ifndef TIZEN_TV
index 9256c3364f9d1f210a6a08ebbdc71c66ded319a3..66d1d8d0760b9b5aa61997a0729761380e72b50e 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.2.68
+Version:    0.2.69
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 2091d19f442a90d16ba9ac10f4a23684b9a0f4a4..5da5a7847839190d5fe319de1094dff9c32f9540 100644 (file)
@@ -664,6 +664,48 @@ int webrtc_set_display(webrtc_h webrtc, unsigned int track_id, webrtc_display_ty
        return ret;
 }
 
+int webrtc_set_display_mode(webrtc_h webrtc, unsigned int track_id, webrtc_display_mode_e mode)
+{
+       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(track_id == 0, WEBRTC_ERROR_INVALID_PARAMETER, "track id is 0");
+       RET_VAL_IF(mode > WEBRTC_DISPLAY_MODE_FULL, WEBRTC_ERROR_INVALID_PARAMETER, "invalid display mode(%d)", mode);
+
+       g_mutex_lock(&_webrtc->mutex);
+
+       if (track_id < TRACK_ID_THRESHOLD_OF_LOOPBACK)
+               ret = _set_display_mode_to_sink(webrtc, track_id, mode);
+       else
+               ret = _set_display_mode_to_loopback(webrtc, track_id, mode);
+
+       g_mutex_unlock(&_webrtc->mutex);
+
+       return ret;
+}
+
+int webrtc_get_display_mode(webrtc_h webrtc, unsigned int track_id, webrtc_display_mode_e *mode)
+{
+       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(track_id == 0, WEBRTC_ERROR_INVALID_PARAMETER, "track id is 0");
+       RET_VAL_IF(mode == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "mode is NULL");
+
+       g_mutex_lock(&_webrtc->mutex);
+
+       if (track_id < TRACK_ID_THRESHOLD_OF_LOOPBACK)
+               ret = _get_display_mode_from_sink(webrtc, track_id, mode);
+       else
+               ret = _get_display_mode_from_loopback(webrtc, track_id, mode);
+
+       g_mutex_unlock(&_webrtc->mutex);
+
+       return ret;
+}
+
 int webrtc_set_encoded_audio_frame_cb(webrtc_h webrtc, webrtc_encoded_frame_cb callback, void *user_data)
 {
        webrtc_s *_webrtc = (webrtc_s*)webrtc;
index 44248a30173910260c69463555642f84093c74db..e4d3b1a5f37a4090e7d27277e22e13fedfba04ec 100644 (file)
@@ -773,3 +773,62 @@ void _release_display(webrtc_display_s *display)
 
        g_free(display);
 }
+
+int _set_display_mode(webrtc_display_s *display, webrtc_display_mode_e mode)
+{
+       RET_VAL_IF(display == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "display is NULL");
+
+       g_mutex_lock(&display->mutex);
+
+       RET_VAL_WITH_UNLOCK_IF(display->mm_display == NULL, WEBRTC_ERROR_INVALID_OPERATION, &display->mutex, "mm_display is NULL");
+       RET_VAL_WITH_UNLOCK_IF(display->surface == NULL, WEBRTC_ERROR_INVALID_OPERATION, &display->mutex, "surface is NULL");
+
+       switch (display->type) {
+       case WEBRTC_DISPLAY_TYPE_OVERLAY:
+       case WEBRTC_DISPLAY_TYPE_ECORE_WL:
+               LOG_INFO("it's %s type, mode[%d]", display->type == WEBRTC_DISPLAY_TYPE_OVERLAY ? "OVERLAY" : "ECORE_WL", mode);
+               RET_VAL_WITH_UNLOCK_IF(display->sink_element == NULL, WEBRTC_ERROR_INVALID_OPERATION, &display->mutex, "sink_element is NULL");
+               RET_VAL_WITH_UNLOCK_IF(!g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(display->sink_element)), "display-geometry-method"),
+                       WEBRTC_ERROR_INVALID_OPERATION, &display->mutex, "could not find 'display-geometry-method'");
+
+               g_object_set(G_OBJECT(display->sink_element), "display-geometry-method", (gint)mode, NULL);
+               display->mode = mode;
+               break;
+
+       case WEBRTC_DISPLAY_TYPE_EVAS:
+               LOG_INFO("it's EVAS type, mode[%d]", mode);
+               RET_VAL_WITH_UNLOCK_IF(mm_display_interface_evas_set_mode(display->mm_display, (int)mode) != 0,
+                       WEBRTC_ERROR_INVALID_OPERATION, &display->mutex, "failed to mm_display_interface_evas_set_mode()");
+
+               display->mode = mode;
+               break;
+
+       default:
+               LOG_ERROR_IF_REACHED("type(%d)", display->type);
+               g_mutex_unlock(&display->mutex);
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       g_mutex_unlock(&display->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int _get_display_mode(webrtc_display_s *display, webrtc_display_mode_e *mode)
+{
+       RET_VAL_IF(display == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "display is NULL");
+       RET_VAL_IF(mode == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "mode is NULL");
+
+       g_mutex_lock(&display->mutex);
+
+       RET_VAL_WITH_UNLOCK_IF(display->mm_display == NULL, WEBRTC_ERROR_INVALID_OPERATION, &display->mutex, "mm_display is NULL");
+       RET_VAL_WITH_UNLOCK_IF(display->surface == NULL, WEBRTC_ERROR_INVALID_OPERATION, &display->mutex, "surface is NULL");
+
+       *mode = display->mode;
+
+       LOG_INFO("mode[%d]", *mode);
+
+       g_mutex_unlock(&display->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
\ No newline at end of file
index 1f927b09bc6fe23097bd6186498936796f8a3e22..3c17a8c4b404c131eb093e43f502f9dcfee1504a 100644 (file)
@@ -163,6 +163,8 @@ static int __build_videosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *sr
                if (sink->display->type == WEBRTC_DISPLAY_TYPE_OVERLAY ||
                        sink->display->type == WEBRTC_DISPLAY_TYPE_ECORE_WL) {
                        gst_video_overlay_set_wl_window_wl_surface_id(GST_VIDEO_OVERLAY(videosink), sink->display->overlay_surface_id);
+                       g_object_set(G_OBJECT(videosink), "display-geometry-method", (gint)0, NULL); /* 0: letter box, 1: origin size, 2: full screen */
+                       sink->display->mode = WEBRTC_DISPLAY_MODE_LETTER_BOX;
 
                } else if (sink->display->type == WEBRTC_DISPLAY_TYPE_EVAS) {
                        g_object_set(G_OBJECT(videosink),
@@ -195,6 +197,8 @@ static int __build_videosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *sr
                return WEBRTC_ERROR_INVALID_OPERATION;
        }
 
+       sink->display->sink_element = videosink;
+
        return ret;
 }
 
@@ -955,3 +959,38 @@ int _set_display_to_sink(webrtc_s *webrtc, unsigned int track_id, unsigned int t
 
        return _apply_display(sink->display);
 }
+
+int _set_display_mode_to_sink(webrtc_s *webrtc, unsigned int track_id, webrtc_display_mode_e mode)
+{
+       webrtc_gst_slot_s *sink;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(track_id == 0, WEBRTC_ERROR_INVALID_PARAMETER, "track id is 0");
+
+       sink = __find_sink_slot_by_id(webrtc, track_id);
+       RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "sink is NULL");
+
+       RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+       RET_VAL_IF((sink->media_types & MEDIA_TYPE_VIDEO) == 0x0, WEBRTC_ERROR_INVALID_OPERATION, "it's not a video track");
+       RET_VAL_IF(_set_display_mode(sink->display, mode) != WEBRTC_ERROR_NONE, WEBRTC_ERROR_INVALID_OPERATION, "failed to _set_display_mode()");
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int _get_display_mode_from_sink(webrtc_s *webrtc, unsigned int track_id, webrtc_display_mode_e *mode)
+{
+       webrtc_gst_slot_s *sink;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(track_id == 0, WEBRTC_ERROR_INVALID_PARAMETER, "track id is 0");
+       RET_VAL_IF(mode == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "mode is NULL");
+
+       sink = __find_sink_slot_by_id(webrtc, track_id);
+       RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "sink is NULL");
+
+       RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+       RET_VAL_IF((sink->media_types & MEDIA_TYPE_VIDEO) == 0x0, WEBRTC_ERROR_INVALID_OPERATION, "it's not a video track");
+       RET_VAL_IF(_get_display_mode(sink->display, mode) != WEBRTC_ERROR_NONE, WEBRTC_ERROR_INVALID_OPERATION, "failed to _get_display_mode()");
+
+       return WEBRTC_ERROR_NONE;
+}
index a7df50460d00b133ac5997c66c496426de735cd9..e82a7b06d111db9518376eae837f989c6a3bed8f 100644 (file)
@@ -3655,3 +3655,40 @@ error:
        source->display = NULL;
        return ret;
 }
+
+int _set_display_mode_to_loopback(webrtc_s *webrtc, unsigned int track_id, webrtc_display_mode_e mode)
+{
+       webrtc_gst_slot_s *source;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(track_id < TRACK_ID_THRESHOLD_OF_LOOPBACK, WEBRTC_ERROR_INVALID_PARAMETER, "invalid track_id(%d)", track_id);
+
+       source = _get_slot_by_id(webrtc->gst.source_slots, track_id / 100);
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(source->av[AV_IDX_VIDEO].render.track_id != track_id, WEBRTC_ERROR_INVALID_PARAMETER, "invalid track_id(%d)", track_id);
+
+       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+       RET_VAL_IF((source->media_types & MEDIA_TYPE_VIDEO) == 0x0, WEBRTC_ERROR_INVALID_OPERATION, "it's not a video track");
+       RET_VAL_IF(_set_display_mode(source->display, mode) != WEBRTC_ERROR_NONE, WEBRTC_ERROR_INVALID_OPERATION, "failed to _set_display_mode()");
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int _get_display_mode_from_loopback(webrtc_s *webrtc, unsigned int track_id, webrtc_display_mode_e *mode)
+{
+       webrtc_gst_slot_s *source;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(track_id < TRACK_ID_THRESHOLD_OF_LOOPBACK, WEBRTC_ERROR_INVALID_PARAMETER, "invalid track_id(%d)", track_id);
+       RET_VAL_IF(mode == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "mode is NULL");
+
+       source = _get_slot_by_id(webrtc->gst.source_slots, track_id / 100);
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(source->av[AV_IDX_VIDEO].render.track_id != track_id, WEBRTC_ERROR_INVALID_PARAMETER, "invalid track_id(%d)", track_id);
+
+       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+       RET_VAL_IF((source->media_types & MEDIA_TYPE_VIDEO) == 0x0, WEBRTC_ERROR_INVALID_OPERATION, "it's not a video track");
+       RET_VAL_IF(_get_display_mode(source->display, mode) != WEBRTC_ERROR_NONE, WEBRTC_ERROR_INVALID_OPERATION, "failed to _get_display_mode()");
+
+       return WEBRTC_ERROR_NONE;
+}
\ No newline at end of file
index 76c46856d057afd1d3381f591a82505cbdb8ab09..7525f9e803d10f370915be868972dff6cf984dbf 100644 (file)
@@ -72,6 +72,8 @@ enum {
        CURRENT_STATUS_MEDIA_PACKET_SOURCE_UNSET_BUFFER_STATE_CHANGED_CB,
        CURRENT_STATUS_MEDIA_PACKET_SOURCE_SET_FORMAT,
        CURRENT_STATUS_SET_DISPLAY_TYPE,
+       CURRENT_STATUS_SET_DISPLAY_MODE,
+       CURRENT_STATUS_GET_DISPLAY_MODE,
        CURRENT_STATUS_MEDIA_SOURCE_SET_AUDIO_LOOPBACK,
        CURRENT_STATUS_MEDIA_SOURCE_SET_VIDEO_LOOPBACK,
        CURRENT_STATUS_DATA_CHANNEL_SEND_STRING,
@@ -854,29 +856,52 @@ static void _webrtc_set_display_type(int index, int type)
        g_print("display type[%d] is set, it'll be applied when starting rendering video.\n", type);
 }
 
+static void _webrtc_set_display_mode(int index, unsigned int track_id, unsigned mode)
+{
+       int ret = WEBRTC_ERROR_NONE;
+
+       ret = webrtc_set_display_mode(g_conns[index].webrtc, track_id, mode);
+       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
+
+       g_print("webrtc_set_display_mode() success, track_id[%u], mode[%u]\n", track_id, mode);
+}
+
+static void _webrtc_get_display_mode(int index, unsigned int track_id)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       webrtc_display_mode_e mode;
+
+       ret = webrtc_get_display_mode(g_conns[index].webrtc, track_id, &mode);
+       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
+
+       g_print("webrtc_get_display_mode() success, track_id[%u], mode[%u]\n", track_id, mode);
+}
+
 static void _webrtc_media_source_set_audio_loopback(int index, unsigned int source_id)
 {
        int ret = WEBRTC_ERROR_NONE;
+       unsigned int track_id;
 
        if (!g_conns[index].render.stream_info) {
                ret = sound_manager_create_stream_information(SOUND_STREAM_TYPE_MEDIA, NULL, NULL, &g_conns[index].render.stream_info);
                RET_IF(ret != SOUND_MANAGER_ERROR_NONE, "failed to sound_manager_create_stream_information(), ret[0x%x]", ret);
        }
 
-       ret = webrtc_media_source_set_audio_loopback(g_conns[index].webrtc, source_id, g_conns[index].render.stream_info, NULL);
+       ret = webrtc_media_source_set_audio_loopback(g_conns[index].webrtc, source_id, g_conns[index].render.stream_info, &track_id);
        RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
 
-       g_print("webrtc_media_source_set_audio_loopback() success, source_id[%u]\n", source_id);
+       g_print("webrtc_media_source_set_audio_loopback() success, source_id[%u] track_id[%u]\n", source_id, track_id);
 }
 
 static void _webrtc_media_source_set_video_loopback(int index, unsigned int source_id)
 {
        int ret = WEBRTC_ERROR_NONE;
+       unsigned int track_id;
 
-       ret = webrtc_media_source_set_video_loopback(g_conns[index].webrtc, source_id, WEBRTC_DISPLAY_TYPE_EVAS, g_eo_mine, NULL);
+       ret = webrtc_media_source_set_video_loopback(g_conns[index].webrtc, source_id, WEBRTC_DISPLAY_TYPE_EVAS, g_eo_mine, &track_id);
        RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
 
-       g_print("webrtc_media_source_set_video_loopback() success, source_id[%u]\n", source_id);
+       g_print("webrtc_media_source_set_video_loopback() success, source_id[%u] track_id[%u]\n", source_id, track_id);
 }
 
 static int __copy_string_arr(gchar *dest_arr, char *string)
@@ -3438,6 +3463,12 @@ void _interpret_main_menu(char *cmd)
                } else if (strncmp(cmd, "dt", 2) == 0) {
                        g_conns[g_conn_index].menu_state = CURRENT_STATUS_SET_DISPLAY_TYPE;
 
+               } else if (strncmp(cmd, "dm", 2) == 0) {
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_SET_DISPLAY_MODE;
+
+               } else if (strncmp(cmd, "gm", 2) == 0) {
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_GET_DISPLAY_MODE;
+
                } else if (strncmp(cmd, "al", 2) == 0) {
                        g_conns[g_conn_index].menu_state = CURRENT_STATUS_MEDIA_SOURCE_SET_AUDIO_LOOPBACK;
 
@@ -3709,7 +3740,9 @@ void display_sub_basic()
        g_print("td. Set transceiver direction\t");
        g_print("gd. Get transceiver direction\n");
        g_print("sf. Set media format to media packet source\n");
-       g_print("dt. Set display type\n");
+       g_print("dt. Set display type\t");
+       g_print("dm. Set display mode\t");
+       g_print("gm. Get display mode\n");
        g_print("al. Set audio loopback\t");
        g_print("vl. Set video loopback\n");
        g_print("cd. Create data channel\t");
@@ -3830,7 +3863,7 @@ static void displaymenu()
                else if (g_conns[g_conn_index].cnt == 1)
                        g_print("*** input media type.(1:audio 2:video)\n");
                else if (g_conns[g_conn_index].cnt == 2)
-                       g_print("*** input transceiver direction.(1:sendonly 2:recvonly, 3:sendrecv)\n");
+                       g_print("*** input transceiver direction.(1:sendonly 2:recvonly 3:sendrecv)\n");
 
        } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_MEDIA_SOURCE_GET_TRANSCEIVER_DIRECTION) {
                if (g_conns[g_conn_index].cnt == 0)
@@ -3839,7 +3872,16 @@ static void displaymenu()
                        g_print("*** input media type.(1:audio 2:video)\n");
 
        } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_SET_DISPLAY_TYPE) {
-               g_print("*** input display type.(1:overlay, 2:evas)\n");
+               g_print("*** input display type.(1:overlay 2:evas)\n");
+
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_SET_DISPLAY_MODE) {
+               if (g_conns[g_conn_index].cnt == 0)
+                       g_print("*** input track id.\n");
+               else if (g_conns[g_conn_index].cnt == 1)
+                       g_print("*** input display mode.(1:letter-box 2:origin size 3:full)\n");
+
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_GET_DISPLAY_MODE) {
+               g_print("*** input track id.\n");
 
        } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_MEDIA_SOURCE_SET_AUDIO_LOOPBACK) {
                g_print("*** input source id.\n");
@@ -4118,6 +4160,30 @@ static void interpret(char *cmd)
                reset_menu_state();
                break;
        }
+       case CURRENT_STATUS_SET_DISPLAY_MODE: {
+               static unsigned int id;
+               value = atoi(cmd);
+
+               switch (g_conns[g_conn_index].cnt) {
+               case 0:
+                       id = value;
+                       g_conns[g_conn_index].cnt++;
+                       break;
+               case 1:
+                       _webrtc_set_display_mode(g_conn_index, id, value - 1);
+                       id = 0;
+                       g_conns[g_conn_index].cnt = 0;
+                       reset_menu_state();
+                       break;
+               }
+               break;
+       }
+       case CURRENT_STATUS_GET_DISPLAY_MODE: {
+               value = atoi(cmd);
+               _webrtc_get_display_mode(g_conn_index, value);
+               reset_menu_state();
+               break;
+       }
        case CURRENT_STATUS_MEDIA_SOURCE_SET_AUDIO_LOOPBACK: {
                value = atoi(cmd);
                _webrtc_media_source_set_audio_loopback(g_conn_index, value);