From: Sangchul Lee Date: Thu, 8 Dec 2022 01:51:10 +0000 (+0900) Subject: webrtc_sink_snapshot: Support PNG format X-Git-Tag: accepted/tizen/7.0/unified/20221220.014756~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2cd5b970d9f01be304a29aaf854f3e7febf2704b;p=platform%2Fcore%2Fapi%2Fwebrtc.git webrtc_sink_snapshot: Support PNG format [Version] 0.3.271 [Issue Type] Add Change-Id: I663ea2cf682492929745e3b9022ec86c9e5c4051 Signed-off-by: Sangchul Lee --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bb8d7f9..dc637db6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ INCLUDE_DIRECTORIES(${INC_DIR}) SET(dependents "dlog glib-2.0 gstreamer-1.0 gstreamer-webrtc-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 \ gstreamer-allocators-1.0 libpulse json-glib-1.0 iniparser mm-common mm-display-interface capi-media-tool \ libtbm libwebsockets cynara-client libsmack capi-system-info libsoup-2.4 bundle capi-media-sound-manager \ - mm-fileinfo mmutil-common mmutil-imgp mmutil-jpeg") + mm-fileinfo mmutil-common mmutil-imgp mmutil-jpeg mmutil-magick") IF(NOT TIZEN_PROFILE_TV) SET(dependents "${dependents} mm-resource-manager") IF(TIZEN_FEATURE_UI) diff --git a/include/webrtc_private.h b/include/webrtc_private.h index 56099f09..88c2ad99 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -541,6 +541,7 @@ typedef struct _webrtc_s { typedef enum snapshot_format_e { SNAPSHOT_FORMAT_RGB24, SNAPSHOT_FORMAT_JPEG, + SNAPSHOT_FORMAT_PNG, } snapshot_format_e; /* FIXME: divide into two slot types or use union */ @@ -611,7 +612,7 @@ typedef struct _webrtc_gst_slot_s { gulong sink_pad_probe_id; GMutex mutex; snapshot_format_e target_format; - int quality; + int option; /* quality for JPEG, compression level for PNG */ } snapshot; struct { @@ -793,7 +794,7 @@ void _remove_probe_from_pad_for_dump(webrtc_gst_slot_s *sink); /* sink capture video frame */ int _init_convert_thread(webrtc_s *webrtc); void _deinit_convert_thread(webrtc_s *webrtc); -int _capture_video_frame_from_sink(webrtc_s *webrtc, unsigned int track_id, snapshot_format_e format, int quality); +int _capture_video_frame_from_sink(webrtc_s *webrtc, unsigned int track_id, snapshot_format_e format, int option); int _capture_video_frame(webrtc_gst_slot_s *sink); /* display */ diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 5c33dab3..568cc16b 100644 --- a/packaging/capi-media-webrtc.spec +++ b/packaging/capi-media-webrtc.spec @@ -1,6 +1,6 @@ Name: capi-media-webrtc Summary: A WebRTC library in Tizen Native API -Version: 0.3.270 +Version: 0.3.271 Release: 0 Group: Multimedia/API License: Apache-2.0 @@ -38,6 +38,7 @@ BuildRequires: pkgconfig(mm-fileinfo) BuildRequires: pkgconfig(mmutil-common) BuildRequires: pkgconfig(mmutil-imgp) BuildRequires: pkgconfig(mmutil-jpeg) +BuildRequires: pkgconfig(mmutil-magick) %if "%{tizen_profile_name}" != "tv" BuildRequires: pkgconfig(mm-resource-manager) BuildRequires: pkgconfig(capi-system-sensor) diff --git a/src/webrtc_sink.c b/src/webrtc_sink.c index fa3cce4d..b2481ccc 100644 --- a/src/webrtc_sink.c +++ b/src/webrtc_sink.c @@ -1308,7 +1308,7 @@ int _get_video_resolution_from_sink(webrtc_s *webrtc, unsigned int track_id, int return WEBRTC_ERROR_NONE; } -int _capture_video_frame_from_sink(webrtc_s *webrtc, unsigned int track_id, snapshot_format_e format, int quality) +int _capture_video_frame_from_sink(webrtc_s *webrtc, unsigned int track_id, snapshot_format_e format, int option) { webrtc_gst_slot_s *sink; @@ -1320,13 +1320,15 @@ int _capture_video_frame_from_sink(webrtc_s *webrtc, unsigned int track_id, snap RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL"); RET_VAL_IF(sink->encoded_frame_cb != NULL, WEBRTC_ERROR_INVALID_OPERATION, "it may be a forwarding sink for encoded frame callback"); RET_VAL_IF((sink->media_types & MEDIA_TYPE_VIDEO) == 0x0, WEBRTC_ERROR_INVALID_OPERATION, "it's not a video track"); + RET_VAL_IF(format == SNAPSHOT_FORMAT_JPEG && (option < 1 || option > 100), WEBRTC_ERROR_INVALID_PARAMETER, "invalid JPEG option value"); + RET_VAL_IF(format == SNAPSHOT_FORMAT_PNG && (option < 0 || option > 9), WEBRTC_ERROR_INVALID_PARAMETER, "invalid PNG option value"); g_mutex_lock(&sink->snapshot.mutex); /* unlock in __remove_probe_from_pad_for_snapshot() */ sink->snapshot.target_format = format; - sink->snapshot.quality = quality; + sink->snapshot.option = option; - LOG_INFO("webrtc[%p] track_id[%u] format[%d] quality[%d]", webrtc, track_id, format, quality); + LOG_INFO("webrtc[%p] track_id[%u] format[%d] option[%d]", webrtc, track_id, format, option); return _capture_video_frame(sink); } diff --git a/src/webrtc_sink_snapshot.c b/src/webrtc_sink_snapshot.c index 8bd794da..99649af8 100644 --- a/src/webrtc_sink_snapshot.c +++ b/src/webrtc_sink_snapshot.c @@ -22,6 +22,7 @@ #include #include #include +#include typedef struct { unsigned char *data; @@ -35,12 +36,13 @@ typedef struct { MMVideoBuffer *vbuffer; bool exit; snapshot_format_e target_format; - int quality; + int option; } queue_data_s; static const char * __format_str[] = { [SNAPSHOT_FORMAT_RGB24] = "RGB24", [SNAPSHOT_FORMAT_JPEG] = "JPEG", + [SNAPSHOT_FORMAT_PNG] = "PNG", }; //LCOV_EXCL_START @@ -157,7 +159,8 @@ static MMVideoBuffer* __get_video_frame_raw_data(GstBuffer *buffer, GstPad *pad, return vbuffer; } -static int __mm_image_convert_colorspace(unsigned char *src_data, size_t src_size, int src_w, int src_h, mm_util_color_format_e src_fmt, mm_util_color_format_e dst_fmt, webrtc_video_frame_s *result) +static int __mm_image_convert_colorspace(unsigned char *src_data, size_t src_size, int src_w, int src_h, mm_util_color_format_e src_fmt, + mm_util_color_format_e dst_fmt, webrtc_video_frame_s *result) { int ret; mm_util_image_h src_image; @@ -188,38 +191,76 @@ static int __mm_image_convert_colorspace(unsigned char *src_data, size_t src_siz return WEBRTC_ERROR_NONE; } -static int __mm_image_encode(unsigned char *src_data, size_t src_size, int src_w, int src_h, mm_util_color_format_e src_fmt, mm_util_img_codec_type codec, int quality, webrtc_video_frame_s *result) +static int __mm_image_encode(unsigned char *src_data, size_t src_size, int src_w, int src_h, mm_util_color_format_e src_fmt, + mm_util_img_codec_type codec, int option, webrtc_video_frame_s *result) { - int ret; + int mm_ret; + int ret = WEBRTC_ERROR_INVALID_OPERATION; mm_util_image_h src_image; + mm_util_enc_opt_h enc_opt = NULL; RET_VAL_IF(src_data == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_data is NULL"); RET_VAL_IF(result == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "result is NULL"); LOG_DEBUG_ENTER(); - ret = mm_image_create_image(src_w, src_h, src_fmt, src_data, src_size, &src_image); - RET_VAL_IF(ret != MM_UTIL_ERROR_NONE, WEBRTC_ERROR_INVALID_OPERATION, "failed to mm_image_create_image()"); - - result->width = src_w; - result->height = src_h; + mm_ret = mm_image_create_image(src_w, src_h, src_fmt, src_data, src_size, &src_image); + RET_VAL_IF(mm_ret != MM_UTIL_ERROR_NONE, WEBRTC_ERROR_INVALID_OPERATION, "failed to mm_image_create_image()"); switch (codec) { case IMG_CODEC_JPEG: - ret = mm_util_encode_to_jpeg_memory(src_image, quality, (void **)&result->data, &result->size); - mm_image_destroy_image(src_image); - RET_VAL_IF(ret != MM_UTIL_ERROR_NONE, WEBRTC_ERROR_INVALID_OPERATION, "failed to mm_util_encode_to_jpeg_memory()"); + /* option value is for quality (1 ~ 100) */ + mm_ret = mm_util_encode_to_jpeg_memory(src_image, option, (void **)&result->data, &result->size); + if (mm_ret != MM_UTIL_ERROR_NONE) { + LOG_ERROR("failed to mm_util_encode_to_jpeg_memory()"); + goto error; + } + break; + case IMG_CODEC_PNG: { + mm_ret = mm_util_enc_opt_create(&enc_opt); + if (mm_ret != MM_UTIL_ERROR_NONE) { + LOG_ERROR("failed to mm_util_enc_opt_create()"); + goto error; + } - LOG_INFO("src[data:%p, size:%zu, %dx%d, fmt:%d] -> dst[data:%p, size:%zu, %ux%u, codec:JPEG, quality:%d]", - src_data, src_size, src_w, src_h, src_fmt, - result->data, result->size, result->width, result->height, quality); - return WEBRTC_ERROR_NONE; + mm_ret = mm_util_enc_opt_set_codec(enc_opt, IMG_CODEC_PNG); + if (mm_ret != MM_UTIL_ERROR_NONE) { + LOG_ERROR("failed to mm_util_enc_opt_set_codec()"); + goto error; + } + + /* option value is for compression level (0 ~ 9) */ + mm_ret = mm_util_enc_opt_set_png_compression(enc_opt, (unsigned int)option); + if (mm_ret != MM_UTIL_ERROR_NONE) { + LOG_ERROR("failed to mm_util_enc_opt_set_png_compression()"); + goto error; + } + mm_ret = mm_util_encode_image_to_buffer(src_image, enc_opt, (void **)&result->data, &result->size); + if (mm_ret != MM_UTIL_ERROR_NONE) { + LOG_ERROR("failed to mm_util_encode_image_to_buffer()"); + goto error; + } + break; + } default: LOG_ERROR_IF_REACHED("codec(%d)", codec); - mm_image_destroy_image(src_image); - return WEBRTC_ERROR_INVALID_PARAMETER; + ret = WEBRTC_ERROR_INVALID_PARAMETER; + goto error; } + + result->width = src_w; + result->height = src_h; + ret = WEBRTC_ERROR_NONE; + + LOG_INFO("src[data:%p, size:%zu, %dx%d, fmt:%d] -> dst[data:%p, size:%zu, %ux%u, codec:%d, option:%d]", + src_data, src_size, src_w, src_h, src_fmt, + result->data, result->size, result->width, result->height, codec, option); +error: + if (enc_opt) + mm_util_enc_opt_destroy(enc_opt); + mm_image_destroy_image(src_image); + return ret; } static int __get_src_size(MMVideoBuffer *vbuffer, mm_util_color_format_e color_format) @@ -333,7 +374,7 @@ static void __dump_rgb24_result(webrtc_video_frame_s *result, unsigned int idx) LOG_WARNING("converted rgb24 frame is dumped to [%s]", file_path); } -static void __dump_encoded_result(webrtc_video_frame_s *result, snapshot_format_e target_format, unsigned int idx) +static void __dump_encoded_result(webrtc_video_frame_s *result, snapshot_format_e target_format, int option, unsigned int idx) { g_autofree gchar *file_path = NULL; g_autoptr(GError) error = NULL; @@ -343,7 +384,7 @@ static void __dump_encoded_result(webrtc_video_frame_s *result, snapshot_format_ if (!result->data) return; - file_path = g_strdup_printf("/tmp/snapshot_%03u_%ux%u_%s.dump", idx, result->width, result->height, __format_str[target_format]); + file_path = g_strdup_printf("/tmp/snapshot_%03u_%ux%u_%s_opt%d.dump", idx, result->width, result->height, __format_str[target_format], option); if (!g_file_set_contents(file_path, (char *)result->data, result->size, &error)) { LOG_ERROR("failed to g_file_set_contents() for %s, error:%s", file_path, error->message); @@ -384,7 +425,7 @@ static GstPadProbeReturn __snapshot_probe_cb(GstPad *pad, GstPadProbeInfo *info, qd->color_format = color_format; qd->vbuffer = vbuffer; qd->target_format = probe_data->slot->snapshot.target_format; - qd->quality = probe_data->slot->snapshot.quality; + qd->option = probe_data->slot->snapshot.option; g_async_queue_push(probe_data->slot->webrtc->snapshot.queue, qd); out: @@ -422,18 +463,20 @@ static gpointer __convert_thread(gpointer data) case SNAPSHOT_FORMAT_RGB24: break; /* skip encoding */ case SNAPSHOT_FORMAT_JPEG: + case SNAPSHOT_FORMAT_PNG: ret = __mm_image_encode(rgb24_frame.data, rgb24_frame.size, rgb24_frame.width, rgb24_frame.height, MM_UTIL_COLOR_RGB24, - IMG_CODEC_JPEG, qd->quality, &encoded_frame); - if (ret != WEBRTC_ERROR_NONE) - LOG_ERROR("failed to __mm_image_encode() for %s", __format_str[qd->target_format]); + (qd->target_format == SNAPSHOT_FORMAT_JPEG) ? IMG_CODEC_JPEG : IMG_CODEC_PNG, qd->option, &encoded_frame); break; default: LOG_ERROR_IF_REACHED("target_format(%d)", qd->target_format); + ret = WEBRTC_ERROR_INVALID_PARAMETER; break; } + if (ret != WEBRTC_ERROR_NONE) + LOG_ERROR("failed to encode image(), ret[0x%x]", ret); #ifdef DUMP_CONVERTED_RESULT __dump_rgb24_result(&rgb24_frame, ++idx); - __dump_encoded_result(&encoded_frame, qd->target_format, idx); + __dump_encoded_result(&encoded_frame, qd->target_format, qd->option, idx); #endif /* TODO: Append data to user callback */ } @@ -484,7 +527,8 @@ int _capture_video_frame(webrtc_gst_slot_s *sink) 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(!(videosink = gst_bin_get_by_name(sink->bin, ELEMENT_NAME_VIDEO_SINK)), WEBRTC_ERROR_INVALID_OPERATION, "could not find video sink element"); + RET_VAL_IF(!(videosink = gst_bin_get_by_name(sink->bin, ELEMENT_NAME_VIDEO_SINK)), WEBRTC_ERROR_INVALID_OPERATION, + "could not find video sink element"); __add_probe_to_pad_for_snapshot(sink, videosink, __snapshot_probe_cb);