Add support for rendering video to EVAS surface 10/248610/11
authorSangchul Lee <sc11.lee@samsung.com>
Tue, 1 Dec 2020 01:11:41 +0000 (10:11 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 9 Dec 2020 04:03:56 +0000 (13:03 +0900)
In case of evas rendering, the handoff signal of fakesink is used
to forward each video frame. After making a media packet based on
the GstBuffer with creating tbm bo and surface, use the evas render
function of mm-display to request it render.

Most of codes are based on the implemenation of player/muse-player
functions except for the server-client structure and zero copy
implementation.

[Version] 0.1.65
[Issue Type] New feature

Change-Id: I138ee1ae01b9de042a1c560f59f4a6a4bee934ed
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
CMakeLists.txt
include/webrtc_private.h
packaging/capi-media-webrtc.spec
src/webrtc.c
src/webrtc_display.c
src/webrtc_sink.c

index c302a4fb46f82fd0a79b753b62e2969220b17333..e6a5639b934911ff1a1d74a22a1d60d36b6faad6 100644 (file)
@@ -11,7 +11,7 @@ SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib")
 SET(INC_DIR include)
 INCLUDE_DIRECTORIES(${INC_DIR})
 
-SET(dependents "dlog glib-2.0 gstreamer-1.0 gstreamer-webrtc-1.0 json-glib-1.0 iniparser mm-common mm-display-interface libtbm")
+SET(dependents "dlog glib-2.0 gstreamer-1.0 gstreamer-webrtc-1.0 gstreamer-video-1.0 json-glib-1.0 iniparser mm-common mm-display-interface capi-media-tool libtbm")
 SET(pc_dependents "capi-base-common" )
 
 INCLUDE(FindPkgConfig)
index edf9233843ec7f0dc8a575a12388e035dc594bd8..c854ef40b7fcccb637ddc3855e733c1494eccd6e 100644 (file)
@@ -226,16 +226,23 @@ typedef struct _webrtc_tbm_s {
 typedef struct _webrtc_display {
        void *object;
        int type;
-       webrtc_callbacks_s video_frame_decoded_cb;
+
+       GMutex mutex;
+       mm_display_interface_h mm_display;
+       webrtc_tbm_s *tbm;
 } webrtc_display_s;
 
+/* FIXME: divide into two slot types */
 typedef struct _webrtc_gst_slot_s {
        unsigned int id;
        GstElement *bin;
        int media_types;    /* values of media_type_e combined with bitwise 'or' */
        int mlines[2];      /* index 0 for audio, 1 for video */
+
        GstElement *webrtcbin;
-       webrtc_display_s display;
+       GList *signals;
+
+       webrtc_display_s *display;
 } webrtc_gst_slot_s;
 
 typedef struct _webrtc_gst_s {
@@ -256,8 +263,6 @@ typedef struct _webrtc_s {
 
        webrtc_gst_s gst;
 
-       mm_display_interface_h mm_display;
-
        gchar *stun_server_url;
        gchar *desc_offer;
        gchar *desc_answer;
@@ -333,9 +338,10 @@ void _sink_slot_destroy_cb(gpointer data);
 void _source_slot_destroy_cb(gpointer data);
 void _generate_dot(webrtc_s *webrtc, const gchar *name);
 
-void _init_display(webrtc_s *webrtc);
-void _deinit_display(webrtc_s *webrtc);
-int _apply_display(webrtc_s *webrtc, webrtc_display_s *display);
+webrtc_display_s *_alloc_display(void);
+void _release_display(webrtc_display_s *display);
+int _apply_display(webrtc_display_s *display);
+void _video_stream_decoded_cb(GstElement *object, GstBuffer *buffer, GstPad *pad, gpointer data);
 
 webrtc_tbm_s *_alloc_tbm(void);
 void _release_tbm(webrtc_tbm_s *tbm);
index e13982c5fc524b3153321969b80b6be31251411f..d7e246aac1184f115bcbf4cf56340eb0f27fe352 100644 (file)
@@ -11,8 +11,10 @@ BuildRequires:  cmake
 BuildRequires:  pkgconfig(dlog)
 BuildRequires:  pkgconfig(glib-2.0)
 BuildRequires:  pkgconfig(capi-base-common)
+BuildRequires:  pkgconfig(capi-media-tool)
 BuildRequires:  pkgconfig(gstreamer-1.0)
 BuildRequires:  pkgconfig(gstreamer-webrtc-1.0)
+BuildRequires:  pkgconfig(gstreamer-video-1.0)
 BuildRequires:  pkgconfig(appcore-efl)
 BuildRequires:  pkgconfig(elementary)
 BuildRequires:  pkgconfig(json-glib-1.0)
index 581a38f39073c979de0cf3dd530201e39c788dfd..ad0908548eb055e95e82ae47a5f4a7e40c7e607a 100644 (file)
@@ -120,7 +120,6 @@ int webrtc_create(webrtc_h *webrtc)
        _gst_init(_webrtc);
        _gst_build_pipeline(_webrtc);
        _init_data_channels(_webrtc);
-       _init_display(_webrtc);
 
        _webrtc->pend_state = WEBRTC_STATE_IDLE;
        _webrtc->state = _webrtc->pend_state;
@@ -147,7 +146,6 @@ int webrtc_destroy(webrtc_h webrtc)
        _webrtc->pend_state = WEBRTC_STATE_IDLE;
        _webrtc->state = _webrtc->pend_state;
 
-       _deinit_display(_webrtc);
        _destroy_data_channels(_webrtc);
        _gst_destroy_pipeline(_webrtc);
        _unload_ini(_webrtc);
index 4b82b4f401cf1228ed1502c25925efe1b8c395fc..59c10c49f8856ec0c538fbd4f84023adae571274 100644 (file)
 #include "webrtc.h"
 #include "webrtc_private.h"
 #include <mm_error.h>
+#include <gst/video/video-info.h>
+#include <tbm_surface_internal.h>
+#include <media_packet.h>
+#include <media_format.h>
 
-int _apply_display(webrtc_s *webrtc, webrtc_display_s *display)
+#define BUFFER_MAX_PLANE_NUM         4
+
+typedef struct _video_decoded_data_s {
+       MMPixelFormatType format;              /**< image format */
+       int width;                             /**< width of video buffer */
+       int height;                            /**< height of video buffer */
+       unsigned int timestamp;                /**< timestamp of buffer (msec)*/
+       unsigned int length_total;             /**< total length of buffer (in byte)*/
+       void *data[BUFFER_MAX_PLANE_NUM];
+       void *bo[BUFFER_MAX_PLANE_NUM];        /**< TBM buffer object */
+       int stride[BUFFER_MAX_PLANE_NUM];      /**< stride of plane */
+       int elevation[BUFFER_MAX_PLANE_NUM];   /**< elevation of plane */
+       int orientation;                       /**< orientation */
+       int bo_size;                           /**< TBM buffer object size */
+       int plane_num;                         /**< number of Plane */
+} video_decoded_data_info_s;
+
+typedef struct _media_packet_finalize_data_s {
+       webrtc_tbm_s *tbm;
+       video_decoded_data_info_s *info;
+} media_packet_finalize_data_s;
+
+static guint32 __convert_fourcc_string_to_value(const gchar *format_name)
+{
+       return format_name[0] | (format_name[1] << 8) | (format_name[2] << 16) | (format_name[3] << 24);
+}
+
+static int __get_pixel_format(unsigned int fourcc)
+{
+       switch (fourcc) {
+       case GST_MAKE_FOURCC('S', 'N', '1', '2'):
+       case GST_MAKE_FOURCC('N', 'V', '1', '2'):
+               return MM_PIXEL_FORMAT_NV12;
+       case GST_MAKE_FOURCC('S', 'T', '1', '2'):
+               return MM_PIXEL_FORMAT_NV12T;
+       case GST_MAKE_FOURCC('S', 'N', '2', '1'):
+       case GST_MAKE_FOURCC('N', 'V', '2', '1'):
+               return MM_PIXEL_FORMAT_NV21;
+       case GST_MAKE_FOURCC('S', 'U', 'Y', 'V'):
+       case GST_MAKE_FOURCC('Y', 'U', 'Y', 'V'):
+       case GST_MAKE_FOURCC('Y', 'U', 'Y', '2'):
+               return MM_PIXEL_FORMAT_YUYV;
+       case GST_MAKE_FOURCC('S', 'Y', 'V', 'Y'):
+       case GST_MAKE_FOURCC('U', 'Y', 'V', 'Y'):
+               return MM_PIXEL_FORMAT_UYVY;
+       case GST_MAKE_FOURCC('S', '4', '2', '0'):
+       case GST_MAKE_FOURCC('I', '4', '2', '0'):
+               return MM_PIXEL_FORMAT_I420;
+       case GST_MAKE_FOURCC('Y', 'V', '1', '2'):
+               return MM_PIXEL_FORMAT_YV12;
+       case GST_MAKE_FOURCC('4', '2', '2', 'P'):
+               return MM_PIXEL_FORMAT_422P;
+       case GST_MAKE_FOURCC('R', 'G', 'B', 'P'):
+               return MM_PIXEL_FORMAT_RGB565;
+       case GST_MAKE_FOURCC('R', 'G', 'B', '3'):
+               return MM_PIXEL_FORMAT_RGB888;
+       case GST_MAKE_FOURCC('A', 'R', 'G', 'B'):
+       case GST_MAKE_FOURCC('x', 'R', 'G', 'B'):
+               return MM_PIXEL_FORMAT_ARGB;
+       case GST_MAKE_FOURCC('B', 'G', 'R', 'A'):
+       case GST_MAKE_FOURCC('B', 'G', 'R', 'x'):
+       case GST_MAKE_FOURCC('S', 'R', '3', '2'):
+               return MM_PIXEL_FORMAT_RGBA;
+       default:
+               LOG_ERROR("not supported fourcc type(%c%c%c%c)", fourcc, fourcc >> 8, fourcc >> 16, fourcc >> 24);
+               return MM_PIXEL_FORMAT_INVALID;
+       }
+}
+
+static int __get_tbm_surface_format(int pixel_format, uint32_t *tbm_format)
+{
+       RET_VAL_IF(pixel_format <= MM_PIXEL_FORMAT_INVALID, WEBRTC_ERROR_INVALID_PARAMETER, "invalid pixel_format(%d)", pixel_format);
+       RET_VAL_IF(pixel_format >= MM_PIXEL_FORMAT_NUM, WEBRTC_ERROR_INVALID_PARAMETER, "invalid pixel_format(%d)", pixel_format);
+       RET_VAL_IF(tbm_format == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "tbm_format is NULL");
+
+       switch (pixel_format) {
+       case MM_PIXEL_FORMAT_NV12:
+               *tbm_format = TBM_FORMAT_NV12;
+               break;
+       case MM_PIXEL_FORMAT_NV12T:
+               *tbm_format = TBM_FORMAT_NV12MT;
+               break;
+       case MM_PIXEL_FORMAT_NV16:
+               *tbm_format = TBM_FORMAT_NV16;
+               break;
+       case MM_PIXEL_FORMAT_NV21:
+               *tbm_format = TBM_FORMAT_NV21;
+               break;
+       case MM_PIXEL_FORMAT_YUYV:
+               *tbm_format = TBM_FORMAT_YUYV;
+               break;
+       case MM_PIXEL_FORMAT_UYVY:
+       case MM_PIXEL_FORMAT_ITLV_JPEG_UYVY:
+               *tbm_format = TBM_FORMAT_UYVY;
+               break;
+       case MM_PIXEL_FORMAT_422P:
+               *tbm_format = TBM_FORMAT_YUV422;
+               break;
+       case MM_PIXEL_FORMAT_I420:
+               *tbm_format = TBM_FORMAT_YUV420;
+               break;
+       case MM_PIXEL_FORMAT_YV12:
+               *tbm_format = TBM_FORMAT_YVU420;
+               break;
+       case MM_PIXEL_FORMAT_RGB565:
+               *tbm_format = TBM_FORMAT_RGB565;
+               break;
+       case MM_PIXEL_FORMAT_RGB888:
+               *tbm_format = TBM_FORMAT_RGB888;
+               break;
+       case MM_PIXEL_FORMAT_RGBA:
+               *tbm_format = TBM_FORMAT_ARGB8888;
+               break;
+       case MM_PIXEL_FORMAT_ARGB:
+               *tbm_format = TBM_FORMAT_ARGB8888;
+               break;
+       default:
+               LOGE("not suppported pixel_format(%d)", pixel_format);
+               return WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       return WEBRTC_ERROR_NONE;
+}
+
+/* These values are based on media_format.h */
+static const char *__get_format_string_from_mime_type(media_format_mimetype_e mimetype)
+{
+       if (mimetype & 0x2510)
+               return "I420";
+       if (mimetype & 0x2520)
+               return "NV12";
+       if (mimetype & 0x2530)
+               return "NV12T";
+       if (mimetype & 0x2540)
+               return "YV12";
+       if (mimetype & 0x2550)
+               return "NV21";
+       if (mimetype & 0x2560)
+               return "NV16";
+       if (mimetype & 0x2570)
+               return "YUYV";
+       if (mimetype & 0x2580)
+               return "UYVY";
+       if (mimetype & 0x2590)
+               return "422P";
+       if (mimetype & 0x25a0)
+               return "RGB565";
+       if (mimetype & 0x25b0)
+               return "RGB888";
+       if (mimetype & 0x25c0)
+               return "RGBA";
+       if (mimetype & 0x25d0)
+               return "ARGB";
+       if (mimetype & 0x25e0)
+               return "BGRA";
+       if (mimetype & 0x25f0)
+               return "Y8";
+
+       return "UNKNOWN";
+}
+
+static int __get_media_packet_mimetype(int pixel_format, media_format_mimetype_e *mimetype)
+{
+       RET_VAL_IF(pixel_format <= MM_PIXEL_FORMAT_INVALID, WEBRTC_ERROR_INVALID_PARAMETER, "invalid pixel_format(%d)", pixel_format);
+       RET_VAL_IF(pixel_format >= MM_PIXEL_FORMAT_NUM, WEBRTC_ERROR_INVALID_PARAMETER, "invalid pixel_format(%d)", pixel_format);
+       RET_VAL_IF(mimetype == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "mimetype is NULL");
+
+       switch (pixel_format) {
+       case MM_PIXEL_FORMAT_NV12:
+               *mimetype = MEDIA_FORMAT_NV12;
+               break;
+       case MM_PIXEL_FORMAT_NV12T:
+               *mimetype = MEDIA_FORMAT_NV12T;
+               break;
+       case MM_PIXEL_FORMAT_NV16:
+               *mimetype = MEDIA_FORMAT_NV16;
+               break;
+       case MM_PIXEL_FORMAT_NV21:
+               *mimetype = MEDIA_FORMAT_NV21;
+               break;
+       case MM_PIXEL_FORMAT_YUYV:
+               *mimetype = MEDIA_FORMAT_YUYV;
+               break;
+       case MM_PIXEL_FORMAT_UYVY:
+       case MM_PIXEL_FORMAT_ITLV_JPEG_UYVY:
+               *mimetype = MEDIA_FORMAT_UYVY;
+               break;
+       case MM_PIXEL_FORMAT_422P:
+               *mimetype = MEDIA_FORMAT_422P;
+               break;
+       case MM_PIXEL_FORMAT_I420:
+               *mimetype = MEDIA_FORMAT_I420;
+               break;
+       case MM_PIXEL_FORMAT_YV12:
+               *mimetype = MEDIA_FORMAT_YV12;
+               break;
+       case MM_PIXEL_FORMAT_RGB565:
+               *mimetype = MEDIA_FORMAT_RGB565;
+               break;
+       case MM_PIXEL_FORMAT_RGB888:
+               *mimetype = MEDIA_FORMAT_RGB888;
+               break;
+       case MM_PIXEL_FORMAT_RGBA:
+               *mimetype = MEDIA_FORMAT_RGBA;
+               break;
+       case MM_PIXEL_FORMAT_ARGB:
+               *mimetype = MEDIA_FORMAT_ARGB;
+               break;
+       default:
+               LOGE("not suppported pixel_format(%d)", pixel_format);
+               return WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static media_packet_rotate_method_e __convert_packet_orient_info(int orientation)
+{
+       media_packet_rotate_method_e rotate = MEDIA_PACKET_ROTATE_IDENTITY;
+
+       switch (orientation) {
+       case 90:
+               rotate = MEDIA_PACKET_ROTATE_90;
+               break;
+       case 180:
+               rotate = MEDIA_PACKET_ROTATE_180;
+               break;
+       case 270:
+               rotate = MEDIA_PACKET_ROTATE_270;
+               break;
+       default:
+               rotate = MEDIA_PACKET_ROTATE_IDENTITY;
+               break;
+       }
+
+       return rotate;
+}
+
+static video_decoded_data_info_s *__get_decoded_data_info_from_pad(GstPad *pad)
+{
+       GstCaps *caps;
+       GstStructure *structure;
+       unsigned int fourcc = 0;
+       const gchar *string_format;
+       video_decoded_data_info_s *info;
+       gint width, height;
+       MMPixelFormatType format;
+       GstVideoInfo vinfo;
+
+       caps = gst_pad_get_current_caps(pad);
+       RET_VAL_IF(caps == NULL, NULL, "caps is NULL");
+
+       structure = gst_caps_get_structure(caps, 0);
+       gst_structure_get_int(structure, "width", &width);
+       gst_structure_get_int(structure, "height", &height);
+       string_format = gst_structure_get_string(structure, "format");
+       if (string_format)
+               fourcc = __convert_fourcc_string_to_value(string_format);
+       format = __get_pixel_format(fourcc);
+       gst_video_info_from_caps(&vinfo, caps);
+       gst_caps_unref(caps);
+
+       RET_VAL_IF(width == 0, NULL, "width is 0");
+       RET_VAL_IF(height == 0, NULL, "height is 0");
+       RET_VAL_IF(format == MM_PIXEL_FORMAT_INVALID, NULL, "pixel format is invalid");
+
+       info = g_new0(video_decoded_data_info_s, 1);
+
+       info->width = width;
+       info->height = height;
+       info->format = format;
+       info->plane_num = GST_VIDEO_INFO_N_PLANES(&vinfo);
+
+       return info;
+}
+
+static bool __swcodec_set_stride_elevation(video_decoded_data_info_s *info)
+{
+       RET_VAL_IF(info == NULL, false, "info is NULL");
+
+       if (info->format == MM_PIXEL_FORMAT_I420) {
+               int ret = TBM_SURFACE_ERROR_NONE;
+               tbm_surface_h ts;
+               tbm_surface_info_s ts_info;
+               int i;
+
+               ts = tbm_surface_create(info->width, info->height, TBM_FORMAT_YUV420);
+               ret = tbm_surface_get_info(ts, &ts_info);
+               tbm_surface_destroy(ts);
+               if (ret != TBM_SURFACE_ERROR_NONE)
+                       return false;
+
+               for (i = 0; i < ts_info.num_planes; i++) {
+                       info->stride[i] = ts_info.planes[i].stride;
+                       info->elevation[i] = ts_info.planes[i].size / ts_info.planes[i].stride;
+               }
+               info->bo_size = ts_info.size;
+
+       } else if (info->format == MM_PIXEL_FORMAT_RGBA) {
+               info->stride[0] = info->width * 4;
+               info->elevation[0] = info->height;
+               info->bo_size = info->stride[0] * info->height;
+
+       } else {
+               LOGE("not supported format(%d)", info->format);
+               return false;
+       }
+
+       return true;
+}
+
+static bool __swcodec_set_bo(webrtc_display_s *display, video_decoded_data_info_s *info, GstMemory *mem)
+{
+       tbm_bo_handle thandle;
+       gboolean is_mapped;
+       int src_stride[BUFFER_MAX_PLANE_NUM] = { 0, };
+       int src_offset[BUFFER_MAX_PLANE_NUM] = { 0, };
+       int dest_offset[BUFFER_MAX_PLANE_NUM] = { 0, };
+       int i = 0;
+       int j = 0;
+       int k = 0;
+       unsigned char *src = NULL;
+       unsigned char *dest = NULL;
+       GstMapInfo mapinfo = GST_MAP_INFO_INIT;
+
+       RET_VAL_IF(display == NULL, false, "display is NULL");
+       RET_VAL_IF(display->tbm == NULL, false, "display->tbm is NULL");
+
+       is_mapped = gst_memory_map(mem, &mapinfo, GST_MAP_READWRITE);
+       RET_VAL_IF(is_mapped == FALSE, false, "is_mapped is FALSE");
+
+       if (mapinfo.data == NULL) {
+               LOG_ERROR("mapinfo.data is NULL");
+               goto ERROR;
+       }
+
+       if (display->tbm->bo_list == NULL)
+               _create_tbm_bo_list(display->tbm, info->bo_size, 10); /* FIXME: use ini file to get list length */
+
+       info->bo[0] = _get_unused_tbm_bo(display->tbm, 10); /* FIXME: use ini file to get timeout sec */
+       if (info->bo[0] == NULL) {
+               LOG_ERROR("failed to _get_unused_tbm_bo()");
+               goto ERROR;
+       }
+
+       thandle = tbm_bo_map(info->bo[0], TBM_DEVICE_CPU, TBM_OPTION_WRITE);
+       if (thandle.ptr == NULL) {
+               LOG_ERROR("thandle pointer is NULL");
+               goto ERROR;
+       }
+
+       if (info->format == MM_PIXEL_FORMAT_I420) {
+               src_stride[0] = GST_ROUND_UP_4(info->width);
+               src_stride[1] = src_stride[2] = GST_ROUND_UP_4(info->width >> 1);
+               src_offset[1] = src_stride[0] * GST_ROUND_UP_2(info->height);
+               src_offset[2] = src_offset[1] + (src_stride[1] * (GST_ROUND_UP_2(info->height) >> 1));
+
+               dest_offset[0] = 0;
+               dest_offset[1] = info->stride[0] * info->elevation[0];
+               dest_offset[2] = dest_offset[1] + info->stride[1] * info->elevation[1];
+
+               for (i = 0; i < 3; i++) {
+                       src = mapinfo.data + src_offset[i];
+                       dest = thandle.ptr + dest_offset[i];
+
+                       if (i > 0)
+                               k = 1;
+
+                       for (j = 0; j < info->height >> k; j++) {
+                               memcpy(dest, src, info->width>>k);
+                               src += src_stride[i];
+                               dest += info->stride[i];
+                       }
+               }
+
+       } else if (info->format == MM_PIXEL_FORMAT_RGBA) {
+               memcpy(thandle.ptr, mapinfo.data, info->bo_size);
+
+       } else {
+               LOG_ERROR("not support format %d", info->format);
+               goto ERROR;
+       }
+
+       tbm_bo_unmap(info->bo[0]);
+       gst_memory_unmap(mem, &mapinfo);
+
+       return true;
+
+ERROR:
+       if (info->bo[0])
+               tbm_bo_unmap(info->bo[0]);
+
+       if (is_mapped)
+               gst_memory_unmap(mem, &mapinfo);
+
+       return false;
+}
+
+static int __media_packet_finalize_cb(media_packet_h m_packet, int error_code, void *user_data)
+{
+       tbm_surface_h ts;
+       media_packet_finalize_data_s *finalize_data = (media_packet_finalize_data_s *)user_data;
+
+       RET_VAL_IF(m_packet == NULL, MEDIA_PACKET_FINALIZE, "m_packet is NULL");
+       RET_VAL_IF(finalize_data == NULL, MEDIA_PACKET_FINALIZE, "finalize_data is NULL");
+
+       if (media_packet_get_tbm_surface(m_packet, &ts) != MEDIA_PACKET_ERROR_NONE)
+               LOG_ERROR("failed to media_packet_get_tbm_surface()");
+
+       if (ts) {
+               LOG_DEBUG("tbm surface[%p] is destroyed", ts);
+               tbm_surface_destroy(ts);
+       }
+
+       if (finalize_data->info->bo[0])
+               _release_tbm_bo(finalize_data->tbm, finalize_data->info->bo[0]);
+
+       g_free(finalize_data->info);
+       g_free(finalize_data);
+
+       return MEDIA_PACKET_FINALIZE;
+}
+
+static media_packet_h __get_media_packet(webrtc_tbm_s *tbm, video_decoded_data_info_s *info, tbm_surface_h ts)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       int m_ret = MEDIA_FORMAT_ERROR_NONE;
+       media_format_h m_format;
+       media_format_mimetype_e mimetype = MEDIA_FORMAT_NV12;
+       media_packet_h m_packet = NULL;
+       media_packet_finalize_data_s *finalize_data = NULL;
+
+       RET_VAL_IF(tbm == NULL, NULL, "tbm is NULL");
+       RET_VAL_IF(info == NULL, NULL, "info is NULL");
+       RET_VAL_IF(ts == NULL, NULL, "ts is NULL");
+
+       ret = __get_media_packet_mimetype(info->format, &mimetype);
+       RET_VAL_IF(ret != WEBRTC_ERROR_NONE, NULL, "failed to __get_media_packet_mimetype()");
+
+       LOG_DEBUG("mimetype[0x%x, %s], resolution[%dx%d], orientation[%d], timestamp[%u]",
+               mimetype, __get_format_string_from_mime_type(mimetype), info->width, info->height, info->orientation, info->timestamp);
+
+       m_ret = media_format_create(&m_format);
+       RET_VAL_IF(m_ret != MEDIA_FORMAT_ERROR_NONE, NULL, "failed to media_format_create()");
+
+       m_ret = media_format_set_video_mime(m_format, mimetype);
+       if (m_ret != MEDIA_FORMAT_ERROR_NONE) {
+               LOG_ERROR("failed to media_format_set_video_mime(), m_ret[0x%x]", m_ret);
+               goto ERROR;
+       }
+
+       m_ret = media_format_set_video_width(m_format, info->width);
+       if (m_ret != MEDIA_FORMAT_ERROR_NONE) {
+               LOG_ERROR("failed to media_format_set_video_width(), m_ret[0x%x]", m_ret);
+               goto ERROR;
+       }
+
+       m_ret = media_format_set_video_height(m_format, info->height);
+       if (m_ret != MEDIA_FORMAT_ERROR_NONE) {
+               LOG_ERROR("failed to media_format_set_video_height(), m_ret[0x%x]", m_ret);
+               goto ERROR;
+       }
+
+       finalize_data = g_new0(media_packet_finalize_data_s, 1);
+       finalize_data->tbm = tbm;
+       finalize_data->info = info;
+
+       m_ret = media_packet_create_from_tbm_surface(m_format, ts, (media_packet_finalize_cb)__media_packet_finalize_cb, (void *)finalize_data, &m_packet);
+       if (m_ret != MEDIA_PACKET_ERROR_NONE) {
+               LOG_ERROR("failed to media_packet_create_from_tbm_surface(), m_ret[0x%x]", m_ret);
+               goto ERROR;
+       }
+
+       if (info->timestamp != 0) {
+               m_ret = media_packet_set_pts(m_packet, (uint64_t)info->timestamp * 1000000);
+               if (m_ret != MEDIA_PACKET_ERROR_NONE)
+                       LOG_ERROR("failed to media_packet_set_pts(), m_ret[0x%x]", m_ret);
+       }
+
+       m_ret = media_packet_set_rotate_method(m_packet, __convert_packet_orient_info(info->orientation));
+       if (m_ret != MEDIA_PACKET_ERROR_NONE)
+               LOG_ERROR("failed to media_packet_set_rotate_method(), m_ret[0x%x]", m_ret);
+
+       media_format_unref(m_format);
+
+       LOG_DEBUG("media packet[%p]", m_packet);
+
+       return m_packet;
+
+ERROR:
+       if (m_format)
+               media_format_unref(m_format);
+
+       if (m_packet)
+               media_packet_destroy(m_packet);
+
+       if (finalize_data)
+               g_free(finalize_data);
+
+       return NULL;
+}
+
+static tbm_surface_h __get_tbm_surface(video_decoded_data_info_s *info)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       tbm_surface_h ts;
+       tbm_surface_info_s ts_info;
+       uint32_t bo_format;
+       int i;
+       int bo_num = 1; /* always 1 for none zerocopy format */
+
+       RET_VAL_IF(info == NULL, NULL, "info is NULL");
+
+       memset(&ts_info, 0, sizeof(tbm_surface_info_s));
+
+       ret = __get_tbm_surface_format(info->format, &bo_format);
+       RET_VAL_IF(ret != WEBRTC_ERROR_NONE, NULL, "failed to __get_tbm_surface_format()");
+
+       ts_info.width = info->width;
+       ts_info.height = info->height;
+       ts_info.format = bo_format;
+       ts_info.bpp = tbm_surface_internal_get_bpp(bo_format);
+       ts_info.num_planes = tbm_surface_internal_get_num_planes(bo_format);
+       ts_info.size = 0;
+
+       for (i = 0; i < ts_info.num_planes; i++) {
+               ts_info.planes[i].stride = info->stride[i];
+               ts_info.planes[i].size = info->stride[i] * info->elevation[i];
+               if (i < bo_num)
+                       ts_info.planes[i].offset = 0;
+               else
+                       ts_info.planes[i].offset = ts_info.planes[i - 1].offset + ts_info.planes[i - 1].size;
+               ts_info.size += ts_info.planes[i].size;
+               if (info->format == MM_PIXEL_FORMAT_RGBA)
+                       ts_info.size = info->stride[0] * info->height;
+       }
+
+       ts = tbm_surface_internal_create_with_bos(&ts_info, (tbm_bo *)info->bo, bo_num);
+       if (ts == NULL) {
+               LOG_ERROR("failed to tbm_surface_internal_create_with_bos()");
+               return NULL;
+       }
+
+       LOG_DEBUG("tbm surface[%p] is created", ts);
+
+       return ts;
+}
+
+static media_packet_h __get_media_packet_from_buffer(webrtc_display_s *display, GstBuffer *buffer, GstPad *pad)
+{
+       video_decoded_data_info_s *info;
+       GstMemory *mem;
+       tbm_surface_h ts;
+       media_packet_h m_packet;
+
+       RET_VAL_IF(display == NULL, NULL, "sink is NULL");
+       RET_VAL_IF(buffer == NULL, NULL, "buffer is NULL");
+       RET_VAL_IF(pad == NULL, NULL, "pad is NULL");
+
+       info = __get_decoded_data_info_from_pad(pad);
+       RET_VAL_IF(info == NULL, NULL, "info is NULL");
+
+       mem = gst_buffer_peek_memory(buffer, 0);
+       info->length_total = gst_memory_get_sizes(mem, NULL, NULL);
+       info->timestamp = (unsigned int)(GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer))); /* nano sec -> milli sec */
+
+       if (!__swcodec_set_stride_elevation(info) || !__swcodec_set_bo(display, info, mem))
+               goto ERROR;
+
+       ts = __get_tbm_surface(info);
+       if (ts == NULL) {
+               LOG_ERROR("failed to __get_tbm_surface()");
+               goto ERROR;
+       }
+
+       m_packet = __get_media_packet(display->tbm, info, ts);
+       if (m_packet == NULL) {
+               LOG_ERROR("failed to __get_media_packet()");
+               goto ERROR;
+       }
+
+       return m_packet;
+
+ERROR:
+       LOG_ERROR("release video data resource");
+
+       if (info->bo[0])
+               _release_tbm_bo(display->tbm, info->bo[0]);
+       g_free(info);
+
+       return NULL;
+}
+
+/* handoff signal handler */
+void _video_stream_decoded_cb(GstElement *object, GstBuffer *buffer, GstPad *pad, gpointer data)
+{
+       webrtc_display_s *display = (webrtc_display_s*)data;
+       media_packet_h packet;
+
+       RET_IF(display == NULL, "sink is NULL");
+
+       LOG_DEBUG("object[%p] buffer[%p] pad[%p] display[%p]", object, buffer, pad, display);
+
+       packet = __get_media_packet_from_buffer(display, buffer, pad);
+       if (packet == NULL) {
+               LOG_ERROR("could not get a media packet");
+               return;
+       }
+
+       mm_display_interface_evas_render(display->mm_display, packet);
+}
+
+static gboolean __set_evas_display_idle_cb(gpointer user_data)
+{
+       int ret = MM_ERROR_NONE;
+       int surface_id;
+       webrtc_display_s *display = (webrtc_display_s*)user_data;
+
+       RET_VAL_IF(display == NULL, G_SOURCE_REMOVE, "display is NULL");
+
+       g_mutex_lock(&display->mutex);
+
+       ret = mm_display_interface_set_display(display->mm_display, MM_DISPLAY_TYPE_EVAS, display->object, &surface_id);
+       if (ret != MM_ERROR_NONE) {
+               LOG_ERROR("failed to mm_display_interface_set_display(), ret[0x%x]", ret);
+               g_mutex_unlock(&display->mutex);
+               return G_SOURCE_REMOVE;
+       }
+
+       LOG_DEBUG("surface id[%d]", surface_id);
+
+       /* FIXE: get mode and rotation value from handle */
+       ret = mm_display_interface_evas_set_mode(display->mm_display, 0); /* 0: letter box, 1: origin size, 2: full screen */
+       RET_VAL_WITH_UNLOCK_IF(ret != MM_ERROR_NONE, G_SOURCE_REMOVE, &display->mutex, "failed to mm_display_interface_evas_set_mode()");
+
+       ret = mm_display_interface_evas_set_rotation(display->mm_display, 0); /* 0: not rotate, 1: 90 degree, 2, 180 degree, 3, 270 degree */
+       RET_VAL_WITH_UNLOCK_IF(ret != MM_ERROR_NONE, G_SOURCE_REMOVE, &display->mutex, "failed to mm_display_interface_evas_set_rotation()");
+
+       ret = mm_display_interface_evas_set_visible(display->mm_display, true);
+       RET_VAL_WITH_UNLOCK_IF(ret != MM_ERROR_NONE, G_SOURCE_REMOVE, &display->mutex, "failed to mm_display_interface_evas_set_visible()");
+
+       g_mutex_unlock(&display->mutex);
+
+       return G_SOURCE_REMOVE;
+}
+
+int _apply_display(webrtc_display_s *display)
 {
-       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
        RET_VAL_IF(display == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "sink is NULL");
 
        switch (display->type) {
@@ -30,8 +679,12 @@ int _apply_display(webrtc_s *webrtc, webrtc_display_s *display)
                break;
 
        case WEBRTC_DISPLAY_TYPE_EVAS:
-               /* TODO: Implemenation */
                LOG_DEBUG("it's EVAS type");
+
+               if (display->tbm == NULL)
+                       display->tbm = _alloc_tbm();
+
+               g_idle_add(__set_evas_display_idle_cb, display);
                break;
 
        default:
@@ -42,20 +695,43 @@ int _apply_display(webrtc_s *webrtc, webrtc_display_s *display)
        return WEBRTC_ERROR_NONE;
 }
 
-void _init_display(webrtc_s *webrtc)
+webrtc_display_s *_alloc_display(void)
 {
-       RET_IF(webrtc == NULL, "webrtc is NULL");
+       webrtc_display_s *display = g_new0(webrtc_display_s, 1);
 
-       if (mm_display_interface_init(&webrtc->mm_display) != MM_ERROR_NONE)
+       g_mutex_init(&display->mutex);
+
+       if (mm_display_interface_init(&display->mm_display) != MM_ERROR_NONE)
                LOG_WARNING("failed to mm_display_interface_init()");
+       else
+               LOG_DEBUG("init mm_display[%p]", display->mm_display);
+
+       LOG_DEBUG("alloc display[%p]", display);
+
+       return display;
 }
 
-void _deinit_display(webrtc_s *webrtc)
+void _release_display(webrtc_display_s *display)
 {
-       RET_IF(webrtc == NULL, "webrtc is NULL");
+       RET_IF(display == NULL, "webrtc is NULL");
 
-       if (webrtc->mm_display) {
-               mm_display_interface_deinit(webrtc->mm_display);
-               webrtc->mm_display = NULL;
+       g_mutex_lock(&display->mutex);
+
+       if (display->mm_display) {
+               LOG_DEBUG("deinit display->mm_display[%p]", display->mm_display);
+               mm_display_interface_deinit(display->mm_display);
+               display->mm_display = NULL;
+       }
+
+       if (display->tbm) {
+               _release_tbm(display->tbm);
+               display->tbm = NULL;
        }
+
+       g_mutex_unlock(&display->mutex);
+       g_mutex_clear(&display->mutex);
+
+       LOG_DEBUG("free display[%p]", display);
+
+       g_free(display);
 }
\ No newline at end of file
index 2ba01e7ca43287d68c6f1445b88a5613f7b6ecae..7a3f1cb73ef3110c6942656805fc9249e3097fbd 100644 (file)
@@ -22,6 +22,7 @@
 #define DEFAULT_ELEMENT_AUDIORESAMPLE "audioresample"
 #define DEFAULT_ELEMENT_VIDEOSINK     "tizenwlsink"
 #define DEFAULT_ELEMENT_AUDIOSINK     "pulsesink"
+#define DEFAULT_ELEMENT_FAKESINK      "fakesink"
 
 static webrtc_gst_slot_s* __find_sink_slot(webrtc_s *webrtc, const gchar *key)
 {
@@ -53,13 +54,21 @@ static int __build_videosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *sr
                return WEBRTC_ERROR_INVALID_OPERATION;
        }
 
-       /* TODO: Implemenation per sink->display.type */
+       /* TODO: Implemenation per sink->display->type */
+       if (sink->display && sink->display->object != NULL && sink->display->type == WEBRTC_DISPLAY_TYPE_EVAS)
+               videosink_factory_name = DEFAULT_ELEMENT_FAKESINK;
+
        /* FIXME: get factory name from ini */
        if (!(videosink = _create_element(videosink_factory_name, NULL))) {
-               LOG_ERROR("failed to create videosink");
+               LOG_ERROR("failed to create videosink[%s]", videosink_factory_name);
                return WEBRTC_ERROR_INVALID_OPERATION;
        }
 
+       if (sink->display && sink->display->object != NULL && sink->display->type == WEBRTC_DISPLAY_TYPE_EVAS) {
+               g_object_set(videosink, "signal-handoffs", TRUE, NULL);
+               _connect_and_append_signal(&sink->signals, (GObject *)videosink, "handoff", G_CALLBACK(_video_stream_decoded_cb), sink->display);
+       }
+
        gst_bin_add_many(GST_BIN(sink->bin), videoconvert, videosink, NULL);
 
        if (!gst_element_sync_state_with_parent(videoconvert)) {
@@ -187,6 +196,7 @@ static void __decodebin_pad_added_cb(GstElement *decodebin, GstPad *new_pad, gpo
        int ret = WEBRTC_ERROR_NONE;
        webrtc_s *webrtc = (webrtc_s *)user_data;
        const gchar *media_type;
+       webrtc_gst_slot_s *sink;
 
        RET_IF(webrtc == NULL, "webrtc is NULL");
 
@@ -196,11 +206,15 @@ static void __decodebin_pad_added_cb(GstElement *decodebin, GstPad *new_pad, gpo
        media_type = gst_structure_get_name(gst_caps_get_structure(gst_pad_get_current_caps(new_pad), 0));
        LOG_INFO("[%s], new_pad[%s], media_type[%s]", GST_ELEMENT_NAME(decodebin), GST_PAD_NAME(new_pad), media_type);
 
+       sink = __find_sink_slot(webrtc, GST_ELEMENT_NAME(decodebin));
+
        if (g_strrstr(media_type, "video")) {
+               sink->media_types |= MEDIA_TYPE_VIDEO;
                __invoke_track_added_cb(webrtc, GST_ELEMENT_NAME(decodebin), true);
                ret = __build_videosink(webrtc, decodebin, new_pad);
 
        } else if (g_strrstr(media_type, "audio")) {
+               sink->media_types |= MEDIA_TYPE_AUDIO;
                __invoke_track_added_cb(webrtc, GST_ELEMENT_NAME(decodebin), false);
                ret = __build_audiosink(webrtc, decodebin, new_pad);
 
@@ -261,8 +275,16 @@ void _sink_slot_destroy_cb(gpointer data)
        LOG_DEBUG("[%s, id:%u, media_types:0x%x] is removed",
                GST_ELEMENT_NAME(sink->bin), sink->id, sink->media_types);
 
+       if (sink->signals) {
+               g_list_free_full(sink->signals, _disconnect_signal);
+               sink->signals = NULL;
+       }
+
        gst_bin_remove(GST_BIN(gst_element_get_parent(sink->bin)), sink->bin);
 
+       if (sink->display)
+               _release_display(sink->display);
+
        g_free(sink);
 }
 
@@ -361,10 +383,15 @@ int _set_display_to_sink(webrtc_s *webrtc, unsigned int track_id, unsigned int t
        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");
 
-       sink->display.type = type;
-       sink->display.object = display;
+       if (sink->display == NULL) {
+               sink->display = _alloc_display();
+               RET_VAL_IF(sink->display == NULL, WEBRTC_ERROR_INVALID_OPERATION, "sink->display is NULL");
+       }
 
        LOG_INFO("type[%d] object[%p]", type, display);
 
-       return _apply_display(webrtc, &sink->display);
+       sink->display->type = type;
+       sink->display->object = display;
+
+       return _apply_display(sink->display);
 }
\ No newline at end of file