From: Sangchul Lee Date: Thu, 8 Dec 2022 07:35:41 +0000 (+0900) Subject: webrtc_internal: Add webrtc_take_snapshot() API X-Git-Tag: accepted/tizen/7.0/unified/20221220.014756~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8bebbddb148fc107bb29c535f9fe73c422c89844;p=platform%2Fcore%2Fapi%2Fwebrtc.git webrtc_internal: Add webrtc_take_snapshot() API [Version] 0.3.272 [Issue Type] Internal API Change-Id: I9750f05f7a64d414b9280644ebb6c84afb071417 Signed-off-by: Sangchul Lee --- diff --git a/include/webrtc_internal.h b/include/webrtc_internal.h index e93702de..cde362f7 100644 --- a/include/webrtc_internal.h +++ b/include/webrtc_internal.h @@ -57,6 +57,25 @@ typedef enum { WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_VIDEO, /**< Custom video */ } webrtc_media_source_type_internal_e; +/** + * @internal + * @brief Enumeration for WebRTC snapshot format. + * @since_tizen 7.0 + */ +typedef enum { + WEBRTC_SNAPSHOT_FORMAT_RAW_RGB24 = 0x01, /**< Raw 24-bit RGB */ + WEBRTC_SNAPSHOT_FORMAT_JPEG = 0x11, /**< JPEG */ + WEBRTC_SNAPSHOT_FORMAT_PNG = 0x12, /**< PNG */ +} webrtc_snapshot_format_e; + +/** + * @internal + * @brief Definition for snapshot option. + * @since_tizen 7.0 + * @see webrtc_take_snapshot() + */ +#define WEBRTC_SNAPSHOT_OPTION_NONE -1 + /** * @internal * @brief Enumeration for WebRTC signaling message type. @@ -72,6 +91,26 @@ typedef enum { SIGNALING_MESSAGE_TYPE_ERROR, /**< Error */ } webrtc_signaling_message_type_e; + +/** + * @internal + * @brief Called when an image is captured by webrtc_take_snapshot(). + * @since_tizen 7.0 + * @remarks The @a webrtc is the same object for which the callback was set.\n + * The @a webrtc should not be released.\n + * The @a data should not be released. The @a data can be used only in the callback. To use outside, make a copy. + * @param[in] webrtc WebRTC handle + * @param[in] track_id The track id + * @param[in] format The snapshot image format + * @param[in] data The snapshot image data captured from @a track_id + * @param[in] width The snapshot image width + * @param[in] height The snapshot image height + * @param[in] size The size of @a data + * @param[in] user_data The user data passed from the callback registration function + * @see webrtc_take_snapshot() + */ +typedef void (*webrtc_snapshot_cb)(webrtc_h webrtc, unsigned int track_id, webrtc_snapshot_format_e format, const char *data, int width, int height, unsigned int size, void *user_data); + /** * @internal * @brief Called when a message to be handled is sent from the remote peer or the signaling server. @@ -157,6 +196,32 @@ int webrtc_get_audio_mute(webrtc_h webrtc, unsigned int track_id, bool *muted); */ int webrtc_get_video_resolution(webrtc_h webrtc, unsigned int track_id, int *width, int *height); +/** + * @internal + * @brief Takes a snapshot from the video track asynchronously. + * @since_tizen 7.0 + * @remarks The registered callback will be invoked in an internal thread of the webrtc.\n + * If webrtc_set_encoded_video_frame_cb() has been called, it will return #WEBRTC_ERROR_INVALID_OPERATION.\n + * @a option only affects when @a format is #WEBRTC_SNAPSHOT_FORMAT_JPEG or #WEBRTC_SNAPSHOT_FORMAT_PNG, + * otherwise #WEBRTC_SNAPSHOT_OPTION_NONE can be used.\n + * If @a format is #WEBRTC_SNAPSHOT_FORMAT_JPEG, @a option is for quality level which range from 1 to 100.\n + * If @a format is #WEBRTC_SNAPSHOT_FORMAT_PNG, @a option is for compression level which range from 0 to 9.\n + * @param[in] webrtc WebRTC handle + * @param[in] track_id The track id + * @param[in] format The snapshot image format + * @param[in] option The option of @a format + * @param[in] callback Callback function pointer + * @param[in] user_data The user data to be passed to the callback function + * @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 webrtc_track_added_cb() must be set by calling webrtc_set_track_added_cb(). + * @post webrtc_snapshot_cb() will be invoked. + */ +int webrtc_take_snapshot(webrtc_h webrtc, unsigned int track_id, webrtc_snapshot_format_e format, int option, webrtc_snapshot_cb callback, void *user_data); + /** * @internal * @brief Sets a video loopback to render the video frames of the media source to an ecore wayland display. diff --git a/include/webrtc_private.h b/include/webrtc_private.h index 88c2ad99..9f3c468d 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -33,7 +33,7 @@ #include #include -#include "webrtc.h" +#include "webrtc_internal.h" #ifdef __cplusplus extern "C" { @@ -538,12 +538,6 @@ typedef struct _webrtc_s { #endif } 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 */ typedef struct _webrtc_gst_slot_s { unsigned int id; @@ -611,8 +605,11 @@ typedef struct _webrtc_gst_slot_s { GstPad *sink_pad; gulong sink_pad_probe_id; GMutex mutex; - snapshot_format_e target_format; - int option; /* quality for JPEG, compression level for PNG */ + unsigned int track_id; + int target_format; + int option; /* quality level for JPEG, compression level for PNG */ + void *callback; + void *user_data; } snapshot; struct { @@ -794,7 +791,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 option); +int _capture_video_frame_from_sink(webrtc_s *webrtc, unsigned int track_id, int format, int option, webrtc_snapshot_cb callback, void *user_data); 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 568cc16b..54803174 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.271 +Version: 0.3.272 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc_internal.c b/src/webrtc_internal.c index 6b268904..d5aaa480 100644 --- a/src/webrtc_internal.c +++ b/src/webrtc_internal.c @@ -86,6 +86,21 @@ int webrtc_get_video_resolution(webrtc_h webrtc, unsigned int track_id, int *wid return _get_video_resolution_from_sink(_webrtc, track_id, width, height); } +int webrtc_take_snapshot(webrtc_h webrtc, unsigned int track_id, webrtc_snapshot_format_e format, int option, webrtc_snapshot_cb callback, void *user_data) +{ + g_autoptr(GMutexLocker) locker = NULL; + webrtc_s *_webrtc = (webrtc_s *)webrtc; + + RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback is NULL"); + + locker = g_mutex_locker_new(&_webrtc->mutex); + + RET_VAL_IF(_webrtc->track_added_cb.callback == NULL, WEBRTC_ERROR_INVALID_OPERATION, "track added callback was not set"); + + return _capture_video_frame_from_sink(_webrtc, track_id, format, option, callback, user_data); +} + int webrtc_media_source_set_video_loopback_to_ecore_wl(webrtc_h webrtc, unsigned int source_id, void *ecore_wl_window, unsigned int *track_id) { int ret = WEBRTC_ERROR_NONE; diff --git a/src/webrtc_sink.c b/src/webrtc_sink.c index b2481ccc..00472cf2 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 option) +int _capture_video_frame_from_sink(webrtc_s *webrtc, unsigned int track_id, int format, int option, webrtc_snapshot_cb callback, void *user_data) { webrtc_gst_slot_s *sink; @@ -1320,15 +1320,19 @@ 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"); + RET_VAL_IF(format == WEBRTC_SNAPSHOT_FORMAT_JPEG && (option < 1 || option > 100), WEBRTC_ERROR_INVALID_PARAMETER, "invalid JPEG option value"); + RET_VAL_IF(format == WEBRTC_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.track_id = track_id; sink->snapshot.target_format = format; sink->snapshot.option = option; + sink->snapshot.callback = callback; + sink->snapshot.user_data = user_data; - LOG_INFO("webrtc[%p] track_id[%u] format[%d] option[%d]", webrtc, track_id, format, option); + LOG_INFO("webrtc[%p] track_id[%u] format[0x%x] option[%d] callback[%p] user_data[%p]", + webrtc, track_id, format, option, callback, user_data); return _capture_video_frame(sink); } diff --git a/src/webrtc_sink_snapshot.c b/src/webrtc_sink_snapshot.c index 99649af8..c896c545 100644 --- a/src/webrtc_sink_snapshot.c +++ b/src/webrtc_sink_snapshot.c @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "webrtc.h" +#include "webrtc_internal.h" #include "webrtc_private.h" #include #include @@ -35,14 +35,17 @@ typedef struct { mm_util_color_format_e color_format; MMVideoBuffer *vbuffer; bool exit; - snapshot_format_e target_format; + unsigned int track_id; + int target_format; int option; + void *callback; + void *user_data; } queue_data_s; static const char * __format_str[] = { - [SNAPSHOT_FORMAT_RGB24] = "RGB24", - [SNAPSHOT_FORMAT_JPEG] = "JPEG", - [SNAPSHOT_FORMAT_PNG] = "PNG", + [WEBRTC_SNAPSHOT_FORMAT_RAW_RGB24] = "RGB24", + [WEBRTC_SNAPSHOT_FORMAT_JPEG] = "JPEG", + [WEBRTC_SNAPSHOT_FORMAT_PNG] = "PNG", }; //LCOV_EXCL_START @@ -374,7 +377,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, int option, unsigned int idx) +static void __dump_encoded_result(webrtc_video_frame_s *result, int target_format, int option, unsigned int idx) { g_autofree gchar *file_path = NULL; g_autoptr(GError) error = NULL; @@ -424,8 +427,11 @@ static GstPadProbeReturn __snapshot_probe_cb(GstPad *pad, GstPadProbeInfo *info, qd = g_new0(queue_data_s, 1); qd->color_format = color_format; qd->vbuffer = vbuffer; + qd->track_id = probe_data->slot->snapshot.track_id; qd->target_format = probe_data->slot->snapshot.target_format; qd->option = probe_data->slot->snapshot.option; + qd->callback = probe_data->slot->snapshot.callback; + qd->user_data = probe_data->slot->snapshot.user_data; g_async_queue_push(probe_data->slot->webrtc->snapshot.queue, qd); out: @@ -460,25 +466,34 @@ static gpointer __convert_thread(gpointer data) /* check target format and encode it if needed */ switch (qd->target_format) { - case SNAPSHOT_FORMAT_RGB24: + case WEBRTC_SNAPSHOT_FORMAT_RAW_RGB24: break; /* skip encoding */ - case SNAPSHOT_FORMAT_JPEG: - case SNAPSHOT_FORMAT_PNG: + case WEBRTC_SNAPSHOT_FORMAT_JPEG: + case WEBRTC_SNAPSHOT_FORMAT_PNG: ret = __mm_image_encode(rgb24_frame.data, rgb24_frame.size, rgb24_frame.width, rgb24_frame.height, MM_UTIL_COLOR_RGB24, - (qd->target_format == SNAPSHOT_FORMAT_JPEG) ? IMG_CODEC_JPEG : IMG_CODEC_PNG, qd->option, &encoded_frame); + (qd->target_format == WEBRTC_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); + LOG_ERROR_IF_REACHED("target_format(0x%x)", qd->target_format); ret = WEBRTC_ERROR_INVALID_PARAMETER; break; } - if (ret != WEBRTC_ERROR_NONE) - LOG_ERROR("failed to encode image(), ret[0x%x]", ret); + if (ret == WEBRTC_ERROR_NONE) { + int width = (int)(encoded_frame.data ? encoded_frame.width : rgb24_frame.width); + int height = (int)(encoded_frame.data ? encoded_frame.height : rgb24_frame.height); + unsigned int size = (unsigned int)(encoded_frame.data ? encoded_frame.size : rgb24_frame.size); #ifdef DUMP_CONVERTED_RESULT - __dump_rgb24_result(&rgb24_frame, ++idx); - __dump_encoded_result(&encoded_frame, qd->target_format, qd->option, idx); + __dump_rgb24_result(&rgb24_frame, ++idx); + __dump_encoded_result(&encoded_frame, qd->target_format, qd->option, idx); #endif - /* TODO: Append data to user callback */ + LOG_DEBUG(">>> callback[%p] user_data[%p] track_id[%u] format[%s] %ux%u", + qd->callback, qd->user_data, qd->track_id, __format_str[qd->target_format], width, height); + ((webrtc_snapshot_cb)(qd->callback))(webrtc, qd->track_id, qd->target_format, encoded_frame.data ? + (const char *)encoded_frame.data : (const char *)rgb24_frame.data, width, height, size, qd->user_data); + LOG_DEBUG("<<< end of the callback"); + } else { + LOG_ERROR("failed to encode image(), skip invoking callback, ret[0x%x]", ret); + } } __release_queue_data(qd);