webrtc_test: Add test cases for media packet source 80/250680/6
authorSangchul Lee <sangchul1011@gmail.com>
Thu, 31 Dec 2020 08:36:48 +0000 (17:36 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 6 Jan 2021 02:46:57 +0000 (11:46 +0900)
Menu items are added as below.
 a. 5:media packet
 sf. Set media format to media packet source
 sm. Set media packet source buffer state changed callback
 um. Unset media packet source buffer state changed callback
 sp. Start pushing packet to media packet source
 tp. Stop pushing packet to media packet source

[Version] 0.1.77
[Issue Type] Test application

Change-Id: I2ca228d956418268d7810a3e34a50f75fc37b41f
Signed-off-by: Sangchul Lee <sangchul1011@gmail.com>
packaging/capi-media-webrtc.spec
test/webrtc_test.c

index 105313e96c40eff1dcb699a508aea8feaf7f76ef..f02d8718d5b988d9df0bacd0264b64b4dba21e69 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.1.76
+Version:    0.1.77
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index e9f3d7b33e6618ec7a77cbcf6ee64176e5c8d141..799052d8865462a8c19a89d31f22152dd65a93d9 100644 (file)
 */
 
 #include <webrtc.h>
+#include <media_format.h>
 #include <appcore-efl.h>
 #include <Elementary.h>
+#include <tbm_surface_internal.h>
 #include <glib.h>
+#include <gst/gst.h>
 #include <libsoup/soup.h>
 #include <json-glib/json-glib.h>
 #include <sys/stat.h>
 #define MAX_STRING_LEN 512
 #define MAX_CHANNEL_LEN 10
 #define MAX_CONNECTION_LEN 4
+#define MAX_MEDIA_PACKET_SOURCE_LEN 4
 
 enum {
        CURRENT_STATUS_MAINMENU,
        CURRENT_STATUS_ADD_MEDIA_SOURCE,
        CURRENT_STATUS_REMOVE_MEDIA_SOURCE,
+       CURRENT_STATUS_MEDIA_PACKET_SOURCE_SET_BUFFER_STATE_CHANGED_CB,
+       CURRENT_STATUS_MEDIA_PACKET_SOURCE_UNSET_BUFFER_STATE_CHANGED_CB,
+       CURRENT_STATUS_MEDIA_PACKET_SOURCE_SET_FORMAT,
        CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION,
        CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION,
        CURRENT_STATUS_SET_DISPLAY_TYPE,
@@ -56,6 +63,8 @@ enum {
        CURRENT_STATUS_CHANGE_CONNECTION,
        CURRENT_STATUS_REQUEST_SESSION,
        CURRENT_STATUS_SEND_LOCAL_DESCRIPTION,
+       CURRENT_STATUS_START_PUSHING_PACKET_TO_MEDIA_PACKET_SOURCE,
+       CURRENT_STATUS_STOP_PUSHING_PACKET_TO_MEDIA_PACKET_SOURCE,
 };
 
 enum {
@@ -97,6 +106,21 @@ typedef struct {
        Evas_Object *layout_main;
 } appdata_s;
 
+typedef struct {
+       unsigned int source_id;
+       media_format_h format;
+
+       webrtc_h webrtc;
+       GstElement *src_pipeline;
+       GstElement *src;
+       GstElement *sink;
+       gulong handoff_signal_id;
+       bool is_overflowed;
+       bool is_stop_requested;
+       GCond cond;
+       GMutex mutex;
+} media_packet_source_s;
+
 typedef struct _connection_s {
        SoupWebsocketConnection *ws_conn;
        gint32 local_peer_id;
@@ -122,6 +146,8 @@ typedef struct _connection_s {
 
        webrtc_display_type_e display_type;
        Evas_Object *eo;
+
+       media_packet_source_s packet_sources[MAX_MEDIA_PACKET_SOURCE_LEN];
 } connection_s;
 
 static gchar g_signaling_server[MAX_STRING_LEN];
@@ -262,6 +288,44 @@ struct appcore_ops ops = {
        .terminate = app_terminate,
 };
 
+static void __disconnect_signal(GObject *obj, gulong signal_id)
+{
+       if (!obj || signal_id == 0) {
+               g_printerr("invalid parameter, obj[%p], signal_id[%lu]\n", obj, signal_id);
+               return;
+       }
+
+       if (g_signal_handler_is_connected(obj, signal_id)) {
+               g_signal_handler_disconnect(obj, signal_id);
+               g_print("signal with id[%lu] is disconnected from object [%s]\n", signal_id, GST_OBJECT_NAME(obj));
+       }
+}
+
+static void __release_packet_source(int conn_idx, unsigned int source_idx)
+{
+       if (conn_idx < 0 || conn_idx >= MAX_CONNECTION_LEN)
+               return;
+       if (source_idx < 0 || source_idx >= MAX_MEDIA_PACKET_SOURCE_LEN)
+               return;
+
+       media_packet_source_s *packet_source = &g_conns[conn_idx].packet_sources[source_idx];
+
+       packet_source->source_id = 0;
+       if (packet_source->format) {
+               media_format_unref(packet_source->format);
+               packet_source->format = NULL;
+       }
+       if (packet_source->handoff_signal_id > 0) {
+               __disconnect_signal((GObject *)packet_source->sink, packet_source->handoff_signal_id);
+               packet_source->handoff_signal_id = 0;
+       }
+       g_clear_object(&packet_source->src_pipeline);
+       packet_source->src_pipeline = NULL;
+
+       g_mutex_clear(&packet_source->mutex);
+       g_cond_clear(&packet_source->cond);
+}
+
 static void _webrtc_create(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
@@ -301,6 +365,9 @@ static void _webrtc_destroy(int index)
                                g_conns[index].recv_channels[i] = NULL;
                }
                g_conns[index].channel_index = 0;
+
+               for (i = 0; i < MAX_MEDIA_PACKET_SOURCE_LEN; i++)
+                       __release_packet_source(index, i);
        }
 }
 
@@ -354,27 +421,173 @@ static void _webrtc_get_state(int index)
                g_print("state: [%s]\n", g_webrtc_state_str[state]);
 }
 
+static int _get_empty_packet_sources_index(int index)
+{
+       int i;
+
+       for (i = 0; i < MAX_MEDIA_PACKET_SOURCE_LEN; i++) {
+               if (g_conns[index].packet_sources[i].source_id == 0)
+                       return i;
+       }
+
+       /* array full */
+       return -1;
+}
+
+static int _find_packet_sources_index(int index, unsigned int source_id)
+{
+       int i;
+
+       if (source_id == 0)
+               return -1;
+
+       for (i = 0; i < MAX_MEDIA_PACKET_SOURCE_LEN; i++) {
+               if (g_conns[index].packet_sources[i].source_id == source_id)
+                       return i;
+       }
+
+       /* not found */
+       return -1;
+}
+
 static void _webrtc_add_media_source(int index, int type)
 {
        int ret = WEBRTC_ERROR_NONE;
        unsigned int source_id = 0;
+       int i;
+
+       if (type == WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET) {
+               i = _get_empty_packet_sources_index(index);
+               if (i == -1) {
+                       g_print("media packet source can be added up to 4\n");
+                       return;
+               }
+       }
 
        ret = webrtc_add_media_source(g_conns[index].webrtc, (webrtc_media_source_type_e)type, &source_id);
-       if (ret != WEBRTC_ERROR_NONE)
+       if (ret != WEBRTC_ERROR_NONE) {
                g_print("failed to webrtc_add_media_source(), ret[0x%x]\n", ret);
-       else
+       } else {
                g_print("webrtc_add_media_source() success, source_id[%u]\n", source_id);
+               if (type == WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET) {
+                       g_conns[index].packet_sources[i].source_id = source_id;
+                       g_mutex_init(&g_conns[index].packet_sources[i].mutex);
+                       g_cond_init(&g_conns[index].packet_sources[i].cond);
+               }
+       }
 }
 
 static void _webrtc_remove_media_source(int index, unsigned int source_id)
 {
        int ret = WEBRTC_ERROR_NONE;
+       int i;
 
        ret = webrtc_remove_media_source(g_conns[index].webrtc, source_id);
-       if (ret != WEBRTC_ERROR_NONE)
+       if (ret != WEBRTC_ERROR_NONE) {
                g_print("failed to webrtc_remove_media_source(), source_id[%u], ret[0x%x]\n", source_id, ret);
-       else
+       } else {
                g_print("webrtc_remove_media_source() success, source_id[%u]\n", source_id);
+               i = _find_packet_sources_index(index, source_id);
+               if (i != -1)
+                       __release_packet_source(index, i);
+       }
+}
+
+#define VIDEO_WIDTH       352
+#define VIDEO_HIGHT       288
+#define VIDEO_FRAME_RATE  30
+#define AUDIO_CHANNEL     1
+#define AUDIO_SAMPLERATE  8000
+
+static int __create_formats(media_format_mimetype_e type, media_format_h *format)
+{
+       int ret = MEDIA_FORMAT_ERROR_NONE;
+
+       if (format == NULL) {
+               g_print("format is NULL\n");
+               return -1;
+       }
+
+       ret = media_format_create(format);
+       if (ret != MEDIA_FORMAT_ERROR_NONE) {
+               g_print("failed to media_format_create()\n");
+               return -1;
+       }
+
+       if (type & MEDIA_FORMAT_AUDIO) {
+               ret = media_format_set_audio_mime(*format, type);
+               ret |= media_format_set_audio_channel(*format, AUDIO_CHANNEL);
+               ret |= media_format_set_audio_samplerate(*format, AUDIO_SAMPLERATE);
+               ret |= media_format_set_audio_bit(*format, 16);
+               if (ret != MEDIA_FORMAT_ERROR_NONE) {
+                       g_print("failed to set audio format\n");
+                       goto error;
+               }
+
+       } else if (type & MEDIA_FORMAT_VIDEO) {
+               ret = media_format_set_video_mime(*format, type);
+               ret |= media_format_set_video_width(*format, VIDEO_WIDTH);
+               ret |= media_format_set_video_height(*format, VIDEO_HIGHT);
+               ret |= media_format_set_video_frame_rate(*format, VIDEO_FRAME_RATE);
+               if (ret != MEDIA_FORMAT_ERROR_NONE) {
+                       g_print("failed to set video format\n");
+                       goto error;
+               }
+
+       } else {
+               g_print("type is not AUDIO or VIDEO\n");
+               goto error;
+       }
+
+       return 0;
+
+error:
+       media_format_unref(*format);
+       return -1;
+}
+
+static void _webrtc_media_packet_source_set_format(int index, unsigned int source_id, int value)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       media_format_h format = NULL;
+       media_format_mimetype_e type;
+
+       switch (value) {
+       case 1:
+               type = MEDIA_FORMAT_I420;
+               break;
+       case 2:
+               type = MEDIA_FORMAT_NV12;
+               break;
+       case 3:
+               type = MEDIA_FORMAT_PCM_S16LE;
+               break;
+       default:
+               g_print("invalid value[%d]\n", value);
+               return;
+       }
+
+       if (__create_formats(type, &format) == -1)
+               return;
+
+       ret = webrtc_media_packet_source_set_format(g_conns[index].webrtc, source_id, format);
+       if (ret != WEBRTC_ERROR_NONE) {
+               g_print("failed to webrtc_set_media_format(), ret[0x%x]\n", ret);
+       } else {
+               int i = _find_packet_sources_index(index, source_id);
+
+               g_print("webrtc_set_media_format() success, source_id[%u], format[%p]\n", source_id, format);
+
+               if (i == -1) {
+                       g_print("could not find media packet source id[%u]\n", source_id);
+                       return;
+               }
+
+               if (g_conns[index].packet_sources[i].format)
+                       media_format_unref(g_conns[index].packet_sources[i].format);
+               g_conns[index].packet_sources[i].format = format;
+               g_conns[index].packet_sources[i].webrtc = g_conns[index].webrtc;
+       }
 }
 
 static void _webrtc_get_transceiver_direction(int index, unsigned int source_id, webrtc_media_type_e media_type)
@@ -888,6 +1101,54 @@ static void _webrtc_unset_track_added_cb(int index)
                g_print("webrtc_unset_track_added_cb() success\n");
 }
 
+static void __media_packet_source_buffer_state_changed_cb(unsigned int source_id, webrtc_media_packet_source_buffer_state_e state, void *user_data)
+{
+       connection_s *conn = (connection_s *)user_data;
+       int i;
+
+       if (conn == NULL) {
+               g_printerr("conn is NULL\n");
+               return;
+       }
+
+       g_print("__media_packet_source_buffer_state_changed_cb() is invoked, source_id[%u], state[%s], conn[%p]\n",
+               source_id, state == WEBRTC_MEDIA_PACKET_SOURCE_BUFFER_STATE_OVERFLOW ? "OVERFLOW" : "UNDERFLOW", conn);
+       for (i = 0; i < MAX_MEDIA_PACKET_SOURCE_LEN; i++)
+               if (conn->packet_sources[i].source_id == source_id)
+                       break;
+       if (i == MAX_MEDIA_PACKET_SOURCE_LEN) {
+               g_printerr("could not find packet source of source_id[%u]", source_id);
+               return;
+       }
+       g_mutex_lock(&conn->packet_sources[i].mutex);
+       conn->packet_sources[i].is_overflowed = (state == WEBRTC_MEDIA_PACKET_SOURCE_BUFFER_STATE_OVERFLOW);
+       g_mutex_unlock(&conn->packet_sources[i].mutex);
+       if (!conn->packet_sources[i].is_overflowed)
+               g_cond_signal(&conn->packet_sources[i].cond);
+}
+
+static void _webrtc_media_packet_source_set_buffer_state_changed_cb(int index, unsigned int source_id)
+{
+       int ret = WEBRTC_ERROR_NONE;
+
+       ret = webrtc_media_packet_source_set_buffer_state_changed_cb(g_conns[index].webrtc, source_id, __media_packet_source_buffer_state_changed_cb, &g_conns[index]);
+       if (ret != WEBRTC_ERROR_NONE)
+               g_print("failed to webrtc_media_packet_source_set_buffer_state_changed_cb()\n");
+       else
+               g_print("webrtc_media_packet_source_set_buffer_state_changed_cb() success\n");
+}
+
+static void _webrtc_media_packet_source_unset_buffer_state_changed_cb(int index, unsigned int source_id)
+{
+       int ret = WEBRTC_ERROR_NONE;
+
+       ret = webrtc_media_packet_source_unset_buffer_state_changed_cb(g_conns[index].webrtc, source_id);
+       if (ret != WEBRTC_ERROR_NONE)
+               g_print("failed to webrtc_media_packet_source_unset_buffer_state_changed_cb()\n");
+       else
+               g_print("webrtc_media_packet_source_unset_buffer_state_changed_cb() success\n");
+}
+
 static void _webrtc_create_offer(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
@@ -1061,6 +1322,369 @@ static void _send_local_description(int index, bool is_offer)
        soup_websocket_connection_send_text(g_conns[index].ws_conn, desc);
 }
 
+gulong _connect_signal(GObject *obj, const char *signal_name, GCallback callback, gpointer user_data)
+{
+       gulong signal_id;
+
+       if (!obj || !signal_name || !callback) {
+               g_printerr("invalid parameter, obj[%p], signal_name[%s], callback[%p]\n", obj, signal_name, callback);
+               return 0;
+       }
+
+       signal_id = g_signal_connect(obj, signal_name, callback, user_data);
+       if (signal_id == 0) {
+               g_printerr("failed to g_signal_connect(), [%s] for object [%s]\n", signal_name, GST_OBJECT_NAME(obj));
+               return 0;
+       }
+
+       g_print("signal [%s] with id[%lu] is connected to object [%s]\n", signal_name, signal_id, GST_OBJECT_NAME(obj));
+
+       return signal_id;
+}
+
+static int __media_packet_finalize_cb(media_packet_h packet, int error_code, void *user_data)
+{
+       g_print("__media_packet_finalize_cb(), packet[%p], error_code[0x%x], user_data[%p]\n", packet, error_code, user_data);
+
+       return MEDIA_PACKET_FINALIZE;
+}
+
+static void __stream_handoff_cb(GstElement *object, GstBuffer *buffer, GstPad *pad, gpointer data)
+{
+       int ret;
+       media_packet_h packet;
+       media_packet_source_s *packet_source = (media_packet_source_s *)data;
+       guchar *data_ptr;
+       gsize size;
+       GstClockTime pts;
+       GstClockTime duration;
+       GstMapInfo buff_info = GST_MAP_INFO_INIT;
+       bool has_tbm_surface;
+       tbm_surface_info_s ts_info;
+
+       if (!object || !buffer || !pad || !packet_source) {
+               g_printerr("invalid parameters, object[%p], buffer[%p], pad[%p], packet_source[%p]\n", object, buffer, pad, packet_source);
+               return;
+       }
+
+       if (packet_source->is_stop_requested) {
+               g_print("stop requested, skip this buffer[%p]\n", buffer);
+               return;
+       }
+
+       g_mutex_lock(&packet_source->mutex);
+       if (packet_source->is_overflowed) {
+               g_print("wait...\n");
+               g_cond_wait(&packet_source->cond, &packet_source->mutex);
+               g_print("wake up...\n");
+       }
+       g_mutex_unlock(&packet_source->mutex);
+
+       size = gst_buffer_get_size(buffer);
+       pts = GST_BUFFER_PTS(buffer);
+       duration = GST_BUFFER_DURATION(buffer);
+
+       g_print("__stream_handoff_cb(), buffer[%p], size[%u], pts[%llu], duration[%llu]\n", buffer, size, pts, duration);
+
+       ret = media_packet_create_alloc(packet_source->format, __media_packet_finalize_cb, packet_source, &packet);
+       if (ret != MEDIA_PACKET_ERROR_NONE) {
+               g_printerr("failed to media_packet_create_alloc()\n");
+               return;
+       }
+
+       gst_buffer_ref(buffer);
+
+       ret = media_packet_set_pts(packet, pts);
+       if (ret != MEDIA_PACKET_ERROR_NONE) {
+               g_printerr("failed to media_packet_set_pts()\n");
+               goto exit;
+       }
+
+       ret = media_packet_set_duration(packet, duration);
+       if (ret != MEDIA_PACKET_ERROR_NONE) {
+               g_printerr("failed to media_packet_set_duration()\n");
+               goto exit;
+       }
+
+       ret = media_packet_set_buffer_size(packet, size);
+       if (ret != MEDIA_PACKET_ERROR_NONE) {
+               g_printerr("failed to media_packet_set_buffer_size()\n");
+               goto exit;
+       }
+
+       ret = media_packet_get_buffer_data_ptr(packet, (void **)&data_ptr);
+       if (ret != MEDIA_PACKET_ERROR_NONE) {
+               g_printerr("failed to media_packet_get_buffer_data_ptr()\n");
+               goto exit;
+       }
+
+       media_packet_has_tbm_surface_buffer(packet, &has_tbm_surface);
+       if (has_tbm_surface) {
+               int ret = TBM_SURFACE_ERROR_NONE;
+               tbm_surface_h ts;
+
+               media_packet_get_tbm_surface(packet, &ts);
+               ret = tbm_surface_get_info(ts, &ts_info);
+               if (ret != TBM_SURFACE_ERROR_NONE) {
+                       g_printerr("failed to tbm_surface_get_info()\n");
+                       goto exit;
+               }
+               g_print("tbm surface[%p, %ux%u, size:%u, format:%u, num_planes:%u] found\n",
+                       ts, ts_info.width, ts_info.height, ts_info.size, ts_info.format, ts_info.num_planes);
+       }
+
+       if (gst_buffer_map(buffer, &buff_info, GST_MAP_READ)) {
+               if (has_tbm_surface) {
+                       int i;
+                       guint8 *ptr = buff_info.data;
+                       for (i = 0; i < ts_info.num_planes; i++) {
+                               g_print("plane[%d][ptr:%p size:%u]\n", i, ts_info.planes[i].ptr, ts_info.planes[i].size);
+                               memcpy(ts_info.planes[i].ptr, ptr, ts_info.planes[i].size);
+                               ptr += ts_info.planes[i].size;
+                       }
+               } else {
+                       memcpy(data_ptr, buff_info.data, size);
+               }
+               gst_buffer_unmap(buffer, &buff_info);
+       } else
+               g_printerr("failed to gst_buffer_map()\n");
+
+       ret = webrtc_media_packet_source_push_packet(packet_source->webrtc, packet_source->source_id, packet);
+       if (ret != WEBRTC_ERROR_NONE)
+               g_printerr("failed to webrtc_media_packet_source_push_packet()\n");
+
+exit:
+       media_packet_destroy(packet);
+       gst_buffer_unref(buffer);
+}
+
+
+static GstCaps *__make_raw_caps(media_format_h format)
+{
+       GstCaps *caps = NULL;
+       media_format_type_e format_type;
+       media_format_mimetype_e mime_type;
+       gchar *format_str;
+
+       if (media_format_get_type(format, &format_type) != MEDIA_FORMAT_ERROR_NONE) {
+               g_printerr("failed to media_format_get_type()\n");
+               return NULL;
+       }
+
+       if (format_type == MEDIA_FORMAT_AUDIO) {
+               int channels;
+               int samplerate;
+
+               if (media_format_get_audio_info(format, &mime_type, &channels, &samplerate, NULL, NULL) != MEDIA_FORMAT_ERROR_NONE) {
+                       g_printerr("failed to media_format_get_audio_info()\n");
+                       return NULL;
+               }
+
+               switch (mime_type) {
+               case MEDIA_FORMAT_PCM_S16LE:
+                       format_str = "S16LE";
+                       break;
+               case MEDIA_FORMAT_PCM_S24LE:
+                       format_str = "S24LE";
+                       break;
+               default:
+                       g_printerr("invalid mime_type[0x%x]\n", mime_type);
+                       return NULL;
+               }
+
+               caps = gst_caps_new_simple("audio/x-raw",
+                                       "format", G_TYPE_STRING, format_str,
+                                       "channels", G_TYPE_INT, channels,
+                                       "rate", G_TYPE_INT, samplerate,
+                                       NULL);
+
+       } else if (format_type == MEDIA_FORMAT_VIDEO) {
+               int width;
+               int height;
+               int framerate;
+
+               if (media_format_get_video_info(format, &mime_type, &width, &height, NULL, NULL) != MEDIA_FORMAT_ERROR_NONE) {
+                       g_printerr("failed to media_format_get_video_info()\n");
+                       return NULL;
+               }
+
+               if (media_format_get_video_frame_rate(format, &framerate) != MEDIA_FORMAT_ERROR_NONE) {
+                       g_printerr("failed to media_format_get_video_frame_rate()\n");
+                       return NULL;
+               }
+
+               switch (mime_type) {
+               case MEDIA_FORMAT_I420:
+                       format_str = "I420";
+                       break;
+               case MEDIA_FORMAT_NV12:
+                       format_str = "NV12";
+                       break;
+               default:
+                       g_printerr("invalid mime_type[0x%x]\n", mime_type);
+                       return NULL;
+               }
+
+               caps = gst_caps_new_simple("video/x-raw",
+                                       "format", G_TYPE_STRING, format_str,
+                                       "framerate", GST_TYPE_FRACTION, framerate, 1,
+                                       "width", G_TYPE_INT, width,
+                                       "height", G_TYPE_INT, height,
+                                       NULL);
+
+       } else {
+               g_printerr("invalid format_type[0x%x]\n", format_type);
+               return NULL;
+       }
+
+       return caps;
+}
+
+static GstElement* __make_src_pipeline(media_packet_source_s *packet_source)
+{
+       media_format_type_e format_type;
+       GstElement *src;
+       GstElement *capsfilter;
+       const char *factory_name_src = "videotestsrc";
+       const char *factory_name_sink;
+       GstCaps *caps;
+       gchar *caps_str;
+
+       if (!packet_source) {
+               g_printerr("packet_source is NULL\n");
+               return NULL;
+       }
+
+       if (!packet_source->format) {
+               g_printerr("format is not set yet\n");
+               return NULL;
+       }
+
+       packet_source->src_pipeline = gst_pipeline_new("src-pipeline");
+       if (!packet_source) {
+               g_printerr("failed to gst_pipeline_new()\n");
+               return NULL;
+       }
+
+       if (media_format_get_type(packet_source->format, &format_type) != MEDIA_FORMAT_ERROR_NONE) {
+               g_printerr("failed to media_format_get_type()\n");
+               return NULL;
+       }
+
+       if (format_type == MEDIA_FORMAT_AUDIO)
+               factory_name_src = "audiotestsrc";
+       else if (format_type == MEDIA_FORMAT_VIDEO)
+               factory_name_src = "videotestsrc";
+
+       src = gst_element_factory_make(factory_name_src, NULL);
+       if (!src) {
+               g_printerr("failed to gst_element_factory_make(), %s\n", factory_name_src);
+               goto error;
+       }
+
+       g_object_set(G_OBJECT(src), "is-live", TRUE, NULL);
+       if (format_type == MEDIA_FORMAT_VIDEO)
+               g_object_set(G_OBJECT(src), "pattern", 18, NULL); /* 18: ball */
+
+       capsfilter = gst_element_factory_make("capsfilter", NULL);
+       if (!capsfilter) {
+               g_printerr("failed to gst_element_factory_make(), capsfilter\n");
+               goto error;
+       }
+       caps = __make_raw_caps(packet_source->format);
+       if (!caps) {
+               g_printerr("failed to __make_raw_caps()\n");
+               goto error;
+       }
+       caps_str = gst_caps_to_string(caps);
+       g_print("capsfilter caps[%s]\n", caps_str);
+       g_free(caps_str);
+
+       g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
+       gst_caps_unref(caps);
+
+       factory_name_sink = "fakesink";
+       packet_source->sink = gst_element_factory_make(factory_name_sink, NULL);
+       if (!packet_source->sink) {
+               g_printerr("failed to gst_element_factory_make(), %s\n", factory_name_sink);
+               goto error;
+       }
+
+       gst_bin_add_many(GST_BIN(packet_source->src_pipeline), src, capsfilter, packet_source->sink, NULL);
+       if (!gst_element_link_many(src, capsfilter, packet_source->sink, NULL)) {
+               g_printerr("failed to gst_element_link_many()\n");
+               goto error;
+       }
+
+       g_object_set(packet_source->sink, "signal-handoffs", TRUE, NULL);
+       packet_source->handoff_signal_id = _connect_signal((GObject *)packet_source->sink, "handoff", G_CALLBACK(__stream_handoff_cb), packet_source);
+       if (packet_source->handoff_signal_id == 0)
+               goto error;
+
+       return packet_source->src_pipeline;
+
+error:
+       gst_object_unref(packet_source->src_pipeline);
+       packet_source->src_pipeline = NULL;
+
+       return NULL;
+}
+
+static void _start_pushing_packet(int index, int source_id)
+{
+       GstStateChangeReturn state_change_ret;
+
+       int i = _find_packet_sources_index(index, source_id);
+       if (i == -1) {
+               g_print("could not find media packet source id[%u]\n", source_id);
+               return;
+       }
+
+       if (g_conns[index].packet_sources[i].format == NULL) {
+               g_print("Please call webrtc_media_packet_source_set_format() first, press 'f'\n");
+               return;
+       }
+
+       if (!g_conns[index].packet_sources[i].src_pipeline) {
+               g_conns[index].packet_sources[i].src_pipeline = __make_src_pipeline(&g_conns[index].packet_sources[i]);
+               if (!g_conns[index].packet_sources[i].src_pipeline)
+                       return;
+       }
+
+       state_change_ret = gst_element_set_state(g_conns[index].packet_sources[i].src_pipeline, GST_STATE_PLAYING);
+       if (state_change_ret == GST_STATE_CHANGE_FAILURE) {
+               g_printerr("failed to set state to PLAYING\n");
+               return;
+       }
+
+       g_conns[index].packet_sources[i].is_stop_requested = false;
+
+       g_print("_start_pushing_packet()\n");
+}
+
+static void _stop_pushing_packet(int index, int source_id)
+{
+       int i = _find_packet_sources_index(index, source_id);
+       if (i == -1) {
+               g_print("could not find media packet source id[%u]\n", source_id);
+               return;
+       }
+
+       if (!g_conns[index].packet_sources[i].src_pipeline) {
+               g_print("already stopped for pushing packet\n");
+               return;
+       }
+
+       if (g_conns[index].packet_sources[i].is_overflowed)
+               g_cond_signal(&g_conns[index].packet_sources[i].cond);
+
+       g_conns[index].packet_sources[i].is_stop_requested = true;
+
+       gst_element_set_state(g_conns[index].packet_sources[i].src_pipeline, GST_STATE_PAUSED);
+
+       g_print("_stop_pushing_packet()\n");
+}
+
 static void __close_websocket(connection_s *conn)
 {
        if (!conn) {
@@ -1324,7 +1948,10 @@ void _interpret_main_menu(char *cmd)
                }
 
        } else if (len == 2) {
-               if (strncmp(cmd, "gd", 2) == 0) {
+               if (strncmp(cmd, "sf", 2) == 0) {
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_MEDIA_PACKET_SOURCE_SET_FORMAT;
+
+               } else if (strncmp(cmd, "gd", 2) == 0) {
                        g_conns[g_conn_index].menu_state = CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION;
 
                } else if (strncmp(cmd, "td", 2) == 0) {
@@ -1384,6 +2011,12 @@ void _interpret_main_menu(char *cmd)
                } else if (strncmp(cmd, "uk", 2) == 0) {
                        _webrtc_unset_track_added_cb(g_conn_index);
 
+               } else if (strncmp(cmd, "sm", 2) == 0) {
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_MEDIA_PACKET_SOURCE_SET_BUFFER_STATE_CHANGED_CB;
+
+               } else if (strncmp(cmd, "um", 2) == 0) {
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_MEDIA_PACKET_SOURCE_UNSET_BUFFER_STATE_CHANGED_CB;
+
                } else if (strncmp(cmd, "co", 2) == 0) {
                        _webrtc_create_offer(g_conn_index);
 
@@ -1420,6 +2053,12 @@ void _interpret_main_menu(char *cmd)
                } else if (strncmp(cmd, "sd", 2) == 0) {
                        g_conns[g_conn_index].menu_state = CURRENT_STATUS_SEND_LOCAL_DESCRIPTION;
 
+               } else if (strncmp(cmd, "sp", 2) == 0) {
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_START_PUSHING_PACKET_TO_MEDIA_PACKET_SOURCE;
+
+               } else if (strncmp(cmd, "tp", 2) == 0) {
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_STOP_PUSHING_PACKET_TO_MEDIA_PACKET_SOURCE;
+
                } else if (strncmp(cmd, "ac", 2) == 0) {
                        _webrtc_add_ice_candidate(g_conn_index);
 
@@ -1429,6 +2068,8 @@ void _interpret_main_menu(char *cmd)
 
        } else if (len == 3) {
                if (strncmp(cmd, "sac", 3) == 0) {
+                       int i;
+
                        _webrtc_set_error_cb(g_conn_index);
                        _webrtc_set_state_changed_cb(g_conn_index);
                        _webrtc_set_negotiation_needed_cb(g_conn_index);
@@ -1436,6 +2077,11 @@ void _interpret_main_menu(char *cmd)
                        _webrtc_set_track_added_cb(g_conn_index);
                        _webrtc_set_data_channel_cb(g_conn_index);
 
+                       for (i = 0; i < MAX_MEDIA_PACKET_SOURCE_LEN; i++) {
+                               if (g_conns[g_conn_index].packet_sources[i].source_id > 0)
+                                       _webrtc_media_packet_source_set_buffer_state_changed_cb(g_conn_index, g_conns[g_conn_index].packet_sources[i].source_id);
+                       }
+
                } else {
                        g_print("unknown menu \n");
                }
@@ -1510,6 +2156,7 @@ 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("sf. Set media format to media packet source\n");
        g_print("gd. Get transceiver direction\t");
        g_print("td. Set transceiver direction\n");
        g_print("dt. Set display type\n");
@@ -1532,6 +2179,8 @@ void display_sub_basic()
        g_print("uk. Unset track added callback\n");
        g_print("sz. Set data channel callback\t");
        g_print("uz. Unset data channel callback\n");
+       g_print("sm. Set media packet source buffer state changed callback\n");
+       g_print("um. Unset media packet source buffer state changed callback\n");
        g_print("------------------------------------- Negotiation ---------------------------------------\n");
        g_print("st. Set STUN server\t");
        g_print("gt. Get STUN server\n");
@@ -1548,6 +2197,8 @@ void display_sub_basic()
        g_print("cs. Connect to the signaling server\n");
        g_print("rs. Request session of remote peer id\n");
        g_print("sd. Send local description\n");
+       g_print("sp. Start pushing packet to media packet source\t");
+       g_print("tp. Stop pushing packet to media packet source\n");
        g_print("-----------------------------------------------------------------------------------------\n");
        g_print("=========================================================================================\n");
 }
@@ -1558,11 +2209,23 @@ static void displaymenu()
                display_sub_basic();
 
        } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_ADD_MEDIA_SOURCE) {
-               g_print("*** input media source type.(1:camera, 2:mic, 3:audiotest, 4:videotest)\n");
+               g_print("*** input media source type.(1:camera, 2:mic, 3:audiotest, 4:videotest, 5:media packet)\n");
 
        } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_REMOVE_MEDIA_SOURCE) {
                g_print("*** input media source id to remove.\n");
 
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_MEDIA_PACKET_SOURCE_SET_BUFFER_STATE_CHANGED_CB) {
+               g_print("*** input media packet source id to set buffer state changed callback.\n");
+
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_MEDIA_PACKET_SOURCE_UNSET_BUFFER_STATE_CHANGED_CB) {
+               g_print("*** input media packet source id to unset buffer state changed callback.\n");
+
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_MEDIA_PACKET_SOURCE_SET_FORMAT) {
+               if (g_conns[g_conn_index].cnt == 0)
+                       g_print("*** input source id.\n");
+               else if (g_conns[g_conn_index].cnt == 1)
+                       g_print("*** input media format.(1:I420 2:NV12 3:PCM_S16LE)\n");
+
        } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION) {
                if (g_conns[g_conn_index].cnt == 0)
                        g_print("*** input source id.\n");
@@ -1610,6 +2273,12 @@ static void displaymenu()
        } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_SEND_LOCAL_DESCRIPTION) {
                g_print("*** input type of local description to send to the server.(1:offer, 2:answer)\n");
 
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_START_PUSHING_PACKET_TO_MEDIA_PACKET_SOURCE) {
+               g_print("*** input media packet source id to start pushing packet.\n");
+
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_STOP_PUSHING_PACKET_TO_MEDIA_PACKET_SOURCE) {
+               g_print("*** input media packet source id to stop pushing packet.\n");
+
        } else {
                g_print("*** unknown status.\n");
                quit_program();
@@ -1655,6 +2324,36 @@ static void interpret(char *cmd)
                reset_menu_state();
                break;
        }
+       case CURRENT_STATUS_MEDIA_PACKET_SOURCE_SET_BUFFER_STATE_CHANGED_CB: {
+               value = atoi(cmd);
+               _webrtc_media_packet_source_set_buffer_state_changed_cb(g_conn_index, value);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_MEDIA_PACKET_SOURCE_UNSET_BUFFER_STATE_CHANGED_CB: {
+               value = atoi(cmd);
+               _webrtc_media_packet_source_unset_buffer_state_changed_cb(g_conn_index, value);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_MEDIA_PACKET_SOURCE_SET_FORMAT: {
+               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_media_packet_source_set_format(g_conn_index, id, value);
+                       id = 0;
+                       g_conns[g_conn_index].cnt = 0;
+                       reset_menu_state();
+                       break;
+               }
+               break;
+       }
        case CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION: {
                static unsigned int id;
                static unsigned int media_type;
@@ -1775,6 +2474,18 @@ static void interpret(char *cmd)
                reset_menu_state();
                break;
        }
+       case CURRENT_STATUS_START_PUSHING_PACKET_TO_MEDIA_PACKET_SOURCE: {
+               value = atoi(cmd);
+               _start_pushing_packet(g_conn_index, value);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_STOP_PUSHING_PACKET_TO_MEDIA_PACKET_SOURCE: {
+               value = atoi(cmd);
+               _stop_pushing_packet(g_conn_index, value);
+               reset_menu_state();
+               break;
+       }
        }
 
        g_timeout_add(100, timeout_menu_display, 0);