webrtc_internal: Add webrtc_take_snapshot() API 06/285506/3
authorSangchul Lee <sc11.lee@samsung.com>
Thu, 8 Dec 2022 07:35:41 +0000 (16:35 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 14 Dec 2022 08:24:00 +0000 (17:24 +0900)
[Version] 0.3.272
[Issue Type] Internal API

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

index e93702deff214026bc2fe8c2c3cec4e250fbc60d..cde362f76892224cc2116ffc10d8587e5198de87 100644 (file)
@@ -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.
index 88c2ad9900afe65f2691183af529127990396993..9f3c468dbb1844fd74184935d6a6cfc726d2b3dc 100644 (file)
@@ -33,7 +33,7 @@
 #include <libwebsockets.h>
 #include <libsoup/soup.h>
 
-#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 */
index 568cc16b7b8075e881bec26ad8a402bc1a0f1470..54803174a52be453b83ad293637ab24cd0502658 100644 (file)
@@ -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
index 6b268904aa57854d74a0900743840fbc24f9a293..d5aaa480ee54e0d5fdabaf330ed0df59d579bc9c 100644 (file)
@@ -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;
index b2481ccc661e035733fedc883bc430d84234e1db..00472cf2975fc6929ab88c0218dacc1bc2c8ccb4 100644 (file)
@@ -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);
 }
index 99649af8e80454a4fca21051b31a4ffb9dee467a..c896c54512c3640092720b957647ea808c1c6e2e 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "webrtc.h"
+#include "webrtc_internal.h"
 #include "webrtc_private.h"
 #include <gst/video/video-info.h>
 #include <mm_error.h>
@@ -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);