Add API - espp_client_take_snapshot() 92/301392/1 accepted/tizen/unified/20231116.175515
authorSangchul Lee <sc11.lee@samsung.com>
Mon, 6 Nov 2023 05:47:44 +0000 (14:47 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 15 Nov 2023 08:24:03 +0000 (08:24 +0000)
[Version] 0.3.12

Change-Id: I34cda79e8dda9bd0c36adbf9ccd86b184f7dbf3a
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
(cherry picked from commit 0155d0be4cbeb92ae5ef8c6e576cd6f36ba77e0f)

14 files changed:
packaging/espp-service.spec
project_def.prop
src/client/espp_service_client.c
src/client/espp_service_client.h
src/client/espp_service_client_event_handler.c
src/client/espp_service_client_priv.h
src/client/espp_service_client_socket.c
src/common/espp_service_ipc.c
src/common/espp_service_ipc.h
src/daemon/espp_service_handler.c
src/daemon/espp_service_priv.h
src/daemon/espp_service_socket.c
src/daemon/meson.build
tizen-manifest.xml

index 464ab88e1c46543702201fe3fbf9b65e5d61ecb8..d766b3a9b133a1888fab8ff06987b90111b26811 100644 (file)
@@ -1,7 +1,7 @@
 Name:       espp-service
 Summary:    ESPP service package which contains client lib. and daemon binary
-Version:    0.3.11
-Release:    1
+Version:    0.3.12
+Release:    0
 Group:      Multimedia/Libraries
 License:    Apache-2.0
 Source0:    %{name}-%{version}.tar.gz
@@ -16,6 +16,10 @@ BuildRequires: pkgconfig(glib-2.0)
 BuildRequires: pkgconfig(gio-2.0)
 BuildRequires: pkgconfig(json-glib-1.0)
 BuildRequires: pkgconfig(esplusplayer)
+BuildRequires: pkgconfig(libtbm)
+BuildRequires: pkgconfig(mm-common)
+BuildRequires: pkgconfig(mmutil-common)
+BuildRequires: pkgconfig(mmutil-imgp)
 %if "%{use_service_app}" == "1"
 BuildRequires: pkgconfig(capi-appfw-service-application)
 BuildRequires: pkgconfig(capi-appfw-application)
index f99fef0c04b6869a0547832d9a30e84ad0e7fa13..3d476e88bb57bb777a062405e0529ebd14590106 100644 (file)
@@ -11,7 +11,7 @@ profile = mobile-7.0
 USER_SRCS = ./src/daemon/*.c ./src/common/*.c
 
 # User Defines
-USER_DEFS = USE_DLOG USE_SERVICE_APP ESPP_SERVICE_VERSION="0.3.11"
+USER_DEFS = USE_DLOG USE_SERVICE_APP ESPP_SERVICE_VERSION="0.3.12"
 
 # User Includes
 USER_INC_DIRS = ./src/daemon ./src/common ./inc ./inc/esplusplayer_capi
index 9e6075e1d12dc8aeaf0a73c135b5ac0e1e7c5b1a..707d845942a2015a88ddffc5f2f9d64fa14d462d 100644 (file)
@@ -217,6 +217,13 @@ int espp_client_set_error_cb(espp_h espp, espp_error_cb callback, void *user_dat
        return ESPP_CLIENT_ERROR_NONE;
 }
 
+static void __snapshot_destroy_cb(gpointer data)
+{
+       espp_callback_s *cb_data = (espp_callback_s *)data;
+       LOG_DEBUG("data[%p, calllback:%p, user_data:%p]", data, cb_data->callback, cb_data->user_data);
+       g_free(data);
+}
+
 int espp_client_create(espp_h *espp)
 {
        espp_s *_espp;
@@ -232,15 +239,14 @@ int espp_client_create(espp_h *espp)
        g_mutex_init(&_espp->mutex);
        g_mutex_init(&_espp->cb_mutex);
 
-       if (espp_service_client_socket_request_create(_espp) != 0) {
-               g_free(_espp);
-               return ESPP_CLIENT_ERROR_INVALID_OPERATION;
-       }
+       _espp->snapshot_cbs = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, __snapshot_destroy_cb);
+
+       if (espp_service_client_socket_request_create(_espp) != 0)
+               goto error;
 
        if (espp_service_client_socket_request_init_event(_espp) != 0) {
                espp_service_client_socket_request_destroy(_espp);
-               g_free(_espp);
-               return ESPP_CLIENT_ERROR_INVALID_OPERATION;
+               goto error;
        }
 
        *espp = _espp;
@@ -248,6 +254,13 @@ int espp_client_create(espp_h *espp)
        LOG_INFO("espp[%p] is created", *espp);
 
        return ESPP_CLIENT_ERROR_NONE;
+
+error:
+       g_hash_table_destroy(_espp->snapshot_cbs);
+       g_mutex_clear(&_espp->mutex);
+       g_mutex_clear(&_espp->cb_mutex);
+       g_free(_espp);
+       return ESPP_CLIENT_ERROR_INVALID_OPERATION;
 }
 
 int espp_client_destroy(espp_h espp)
@@ -268,6 +281,11 @@ int espp_client_destroy(espp_h espp)
                return ESPP_CLIENT_ERROR_INVALID_OPERATION;
        }
 
+       if (_espp->snapshot_cbs) {
+               g_hash_table_destroy(_espp->snapshot_cbs);
+               _espp->snapshot_cbs = NULL;
+       }
+
        g_mutex_unlock(&_espp->mutex);
        g_mutex_clear(&_espp->mutex);
        g_mutex_clear(&_espp->cb_mutex);
@@ -707,3 +725,38 @@ int espp_client_set_decoded_video_frame_buffer_type(espp_h espp, espp_decoded_vi
 
        return ESPP_CLIENT_ERROR_NONE;
 }
+
+int espp_client_take_snapshot(espp_h espp, espp_snapshot_cb callback, void *user_data)
+{
+       espp_s *_espp = (espp_s *)espp;
+       espp_callback_s *cb_data;
+       g_autoptr(GMutexLocker) locker = NULL;
+       gint *id;
+
+       RET_VAL_IF(!espp, ESPP_CLIENT_ERROR_INVALID_PARAMETER, "espp is NULL");
+       RET_VAL_IF(!callback, ESPP_CLIENT_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+       locker = g_mutex_locker_new(&_espp->mutex);
+
+       cb_data = g_new0(espp_callback_s, 1);
+       cb_data->callback = callback;
+       cb_data->user_data = user_data;
+
+       id = g_new(gint, 1);
+       *id = ++_espp->snapshot_cb_id;
+       if (!g_hash_table_insert(_espp->snapshot_cbs, id, (gpointer)cb_data)) {
+               LOG_ERROR("should not be reached here, snapshot_cb_id[%d] already exist", _espp->snapshot_cb_id);
+               /* Due to the imple. of g_hash_table_insert() in this case, we need to alloc again for id. */
+               id = g_new(gint, 1);
+               *id = _espp->snapshot_cb_id--;
+               g_hash_table_remove(_espp->snapshot_cbs, id);
+               return ESPP_CLIENT_ERROR_INVALID_OPERATION;
+       }
+
+       if (espp_service_client_socket_request_take_snapshot(_espp, _espp->snapshot_cb_id) != 0)
+               return ESPP_CLIENT_ERROR_INVALID_OPERATION;
+
+       LOG_INFO("espp[%p] snapshot_cb_id[%d] callback[%p] user_data[%p]", espp, _espp->snapshot_cb_id, callback, user_data);
+
+       return ESPP_CLIENT_ERROR_NONE;
+}
index 3716697d17e52ca0e997230ab92f5124b270d379..36c0a19208bc76bab6f96072ce5b699b4b4b820d 100644 (file)
@@ -278,6 +278,21 @@ typedef void (*espp_eos_cb)(void *user_data);
  */
 typedef void (*espp_buffer_status_cb)(espp_stream_type_e stream_type, espp_buffer_status_e buffer_status, void *user_data);
 
+/**
+ * @brief Called when an image is captured by espp_client_take_snapshot().
+ * @remarks The @a espp is the same object for which the callback was set.\n
+ *          The @a espp 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] espp       ESPP service client handle
+ * @param[in] data       The snapshot image data (24-bit RGB)
+ * @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 espp_client_take_snapshot()
+ */
+typedef void (*espp_snapshot_cb)(espp_h espp, const char *data, int width, int height, unsigned int size, void *user_data);
+
 /**
  * @brief Called when a H/W resource of the ESPP service client handle has been conflicted.
  * @param[in] user_data    The user data passed from the callback registration function
@@ -833,6 +848,21 @@ int espp_client_set_low_latency_mode(espp_h espp, espp_low_latency_mode_e mode);
  */
 int espp_client_set_decoded_video_frame_buffer_type(espp_h espp, espp_decoded_video_frame_buffer_type_e type);
 
+/**
+ * @brief Takes a snapshot asynchronously.
+ * @param[in] espp     ESPP service client handle
+ * @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 #ESPP_CLIENT_ERROR_NONE Successful
+ * @retval #ESPP_CLIENT_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPP_CLIENT_ERROR_INVALID_OPERATION Invalid operation
+ * @pre #ESPP_DECODED_VIDEO_FRAME_BUFFER_TYPE_MANUAL_COPY must be set by espp_client_set_decoded_video_frame_buffer_type() before calling this function.
+ * @see espp_client_set_decoded_video_frame_buffer_type()
+ */
+int espp_client_take_snapshot(espp_h espp, espp_snapshot_cb callback, void *user_data);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index a3f15e590bbb6af1eb4edb9bca47742bdee21ac9..47ce45ab7cf61e0e412f4fcf7be0085cb8436fb5 100644 (file)
@@ -213,6 +213,77 @@ static void __handle_event_cb_error(espp_s *espp, espp_service_data_from_server_
        result->ret = 0;
 }
 
+static int __read_buffer(int fd, gchar *buffer, uint32_t size)
+{
+       int ret;
+       uint32_t total = 0;
+       char str_error[MAX_ERROR_LEN] = {'\0',};
+
+       ASSERT(fd >= 0);
+       ASSERT(buffer);
+
+       do {
+               if ((ret = read(fd, buffer + total, size - total)) < 0) {
+                       strerror_r(errno, str_error, sizeof(str_error));
+                       LOG_ERROR("failed to read(), fd[%d], ret[%d], err: %s", fd, ret, str_error);
+                       break;
+               }
+               total += ret;
+       } while (total < size);
+
+       if (total != size) {
+               LOG_ERROR("total[%u], expected size[%u]", total, size);
+               return -1;
+       }
+
+       LOG_DEBUG("fd[%d] buffer[%p, size:%u]", fd, buffer, size);
+
+       return 0;
+}
+
+static void __handle_event_cb_snapshot(espp_s *espp, espp_service_data_from_server_s *data, espp_service_data_from_client_s *result)
+{
+       int ret;
+       int32_t cb_id;
+       uint32_t width;
+       uint32_t height;
+       uint32_t size;
+       g_autofree gchar *buffer = NULL;
+       g_autofree gint *id = NULL;
+       espp_callback_s *cb_data;
+
+       ASSERT(espp);
+       ASSERT(data);
+       ASSERT(result);
+
+       result->ret = -1;
+
+       ret = espp_service_client_msg_parse_params(data->params, data->event, &cb_id, &width, &height, &size);
+       if (ret != 0)
+               return;
+
+       LOG_INFO("cb params[cb_id:%d, width:%u, height:%u, size:%u]", cb_id, width, height, size);
+
+       buffer = g_malloc0(size);
+       if (__read_buffer(espp->event_fd, buffer, size) != 0)
+               return;
+
+       id = g_new(gint, 1);
+       *id = cb_id;
+       cb_data = g_hash_table_lookup(espp->snapshot_cbs, id);
+       if (!cb_data) {
+               LOG_ERROR("could not find cb_data of cb_id[%d]", cb_id);
+               return;
+       }
+
+       if (cb_data->callback) {
+               LOG_DEBUG(">>> cb_id[%d] callback[%p] user_data[%p]", cb_id, cb_data->callback, cb_data->user_data);
+               ((espp_snapshot_cb)(cb_data->callback))((espp_h)espp, (const char *)buffer, (int)width, (int)height, (unsigned int)size, cb_data->user_data);
+               LOG_DEBUG("<<< end of the callback");
+       }
+       result->ret = 0;
+}
+
 static func_handler handlers[] = {
        [ESPP_SERVICE_EVENT_MSG] = __handle_event_msg,
        [ESPP_SERVICE_EVENT_CB_READY_TO_PREPARE] = __handle_event_cb_ready_to_prepare,
@@ -223,6 +294,7 @@ static func_handler handlers[] = {
        [ESPP_SERVICE_EVENT_CB_BUFFER_STATUS] = __handle_event_cb_buffer_status,
        [ESPP_SERVICE_EVENT_CB_RESOURCE_CONFLICTED] = __handle_event_cb_resource_conflicted,
        [ESPP_SERVICE_EVENT_CB_ERROR] = __handle_event_cb_error,
+       [ESPP_SERVICE_EVENT_CB_SNAPSHOT] = __handle_event_cb_snapshot,
 };
 
 static void __func_handler(espp_s *espp, espp_service_data_from_server_s *data, espp_service_data_from_client_s *result)
index 002cfc601d3de0cce589f93d930f13491d33d768..73ce028a790e6a0bd39b5ff66ee590681f51d49e 100644 (file)
@@ -51,6 +51,7 @@ typedef struct _espp_s {
 
        int fd;
        int event_fd;
+       int snapshot_cb_id;
 
        struct {
                GThread *thread;
@@ -65,6 +66,7 @@ typedef struct _espp_s {
        espp_callback_s buffer_status_cb;
        espp_callback_s resource_conflicted_cb;
        espp_callback_s error_cb;
+       GHashTable *snapshot_cbs;
 } espp_s;
 
 /* socket */
@@ -99,6 +101,7 @@ int espp_service_client_socket_request_submit_eos_packet(espp_s *espp, espp_stre
 int espp_service_client_socket_request_set_buffer_size(espp_s *espp, espp_buffer_size_type_e size_type, uint64_t size);
 int espp_service_client_socket_request_set_low_latency_mode(espp_s *espp, espp_low_latency_mode_e mode);
 int espp_service_client_socket_request_set_decoded_video_frame_buffer_type(espp_s *espp, espp_decoded_video_frame_buffer_type_e type);
+int espp_service_client_socket_request_take_snapshot(espp_s *espp, int id);
 
 /* event handler */
 gpointer espp_service_client_event_handler_thread_func(gpointer user_data);
index 9b640622fa8fcf40770b42c0760e2c27d8b29a6a..b46f7f958e20e3e1bfc76015827d5325f6a59208 100644 (file)
@@ -879,3 +879,23 @@ int espp_service_client_socket_request_set_decoded_video_frame_buffer_type(espp_
 
        return 0;
 }
+
+int espp_service_client_socket_request_take_snapshot(espp_s *espp, int id)
+{
+       espp_service_data_from_client_s data;
+       espp_service_data_from_server_s result;
+
+       ASSERT(espp);
+       RET_VAL_IF(espp->fd == -1, -1, "fd is -1");
+
+       FILL_SOCKET_MSG_REQUEST(data, ESPP_SERVICE_REQUEST_TAKE_SNAPSHOT);
+       FILL_SOCKET_MSG_PARAMS(data, ESPP_SERVICE_REQUEST_TAKE_SNAPSHOT, "id", id);
+       if (send_data(espp->fd, &data, &result) != 0)
+               return -1;
+
+       RET_VAL_IF_SERVER_RESULT_ERROR(result, -1);
+
+       LOG_DEBUG("espp[%p], fd[%d]", espp, espp->fd);
+
+       return 0;
+}
index 11188f20f8a47b15230940c1440cab37d8710206..77c5f75127da849a0a7cb850b5410c9daaaa566a 100644 (file)
@@ -54,6 +54,7 @@ espp_service_ipc_data_s requests[] = {
        [ESPP_SERVICE_REQUEST_SET_BUFFER_SIZE] = { "SetBufferSize", "ik" },
        [ESPP_SERVICE_REQUEST_SET_LOW_LATENCY_MODE] = { "SetLowLatencyMode", "i" },
        [ESPP_SERVICE_REQUEST_SET_DECODED_VIDEO_FRAME_BUFFER_TYPE] = { "SetDecodedVideoFrameBufferType", "i" },
+       [ESPP_SERVICE_REQUEST_TAKE_SNAPSHOT] = { "TakeSnapshot", "i" },
        [ESPP_SERVICE_REQUEST_SET_CALLBACK] = { "SetCallback", "i" },
 };
 
@@ -67,6 +68,7 @@ espp_service_ipc_data_s events[] = {
        [ESPP_SERVICE_EVENT_CB_BUFFER_STATUS] = { "BufferStatusCB", "ii" },
        [ESPP_SERVICE_EVENT_CB_RESOURCE_CONFLICTED] = { "PrepareAsyncDoneCB", NULL },
        [ESPP_SERVICE_EVENT_CB_ERROR] = { "ErrorCB", "i" },
+       [ESPP_SERVICE_EVENT_CB_SNAPSHOT] = { "SnapshotCB", "iuuu" },
 };
 
 const char *data_type_strs[] = {
index 60282329d7388c55f014d6fdc1a2cca18573fef5..c1fcf8f0919d86a00c1db2625dd7631d01f34a27 100644 (file)
@@ -55,6 +55,7 @@ typedef enum {
        ESPP_SERVICE_REQUEST_SET_BUFFER_SIZE,
        ESPP_SERVICE_REQUEST_SET_LOW_LATENCY_MODE,
        ESPP_SERVICE_REQUEST_SET_DECODED_VIDEO_FRAME_BUFFER_TYPE,
+       ESPP_SERVICE_REQUEST_TAKE_SNAPSHOT,
        ESPP_SERVICE_REQUEST_SET_CALLBACK,
        ESPP_SERVICE_REQUEST_NUM,
 } espp_service_request_e;
@@ -69,6 +70,7 @@ typedef enum {
        ESPP_SERVICE_EVENT_CB_BUFFER_STATUS,
        ESPP_SERVICE_EVENT_CB_RESOURCE_CONFLICTED,
        ESPP_SERVICE_EVENT_CB_ERROR,
+       ESPP_SERVICE_EVENT_CB_SNAPSHOT,
        ESPP_SERVICE_EVENT_CB_NUM,
 } espp_service_event_e;
 
index 5ebe6043fe72b424c66d5d71fa029cf1906e9a3d..46993c32a5b670fc3c7b01035357a3bf3425da20 100644 (file)
 #include <esplusplayer_capi.h>
 #include <esplusplayer_internal.h>
 #include <inttypes.h>
+#include <tbm_surface.h>
+#include <mm_util_image.h>
+#include <mm_util_imgp.h>
+#include <mm_error.h>
+
+#define USECONDS_TO_MSECONDS(usec) ((usec) / G_GINT64_CONSTANT (1000))
+#define C(b,m)              (((b) >> (m)) & 0xFF)
+#define FOURCC_STR(id)      C(id,0), C(id,8), C(id,16), C(id,24)
 
 typedef int (*set_cb_func) (esplusplayer_handle handle, void *callback, void *user_data);
 typedef void (*func_handler) (handler_userdata_s *hdata, espp_service_data_from_client_s *data, espp_service_data_from_server_s *result);
 
+static gpointer __snapshot_work_thread(gpointer data);
+
 typedef struct {
        set_cb_func set_cb;
        void *callback;
@@ -31,8 +41,20 @@ typedef struct {
        int fd;
        int event_fd;
        esplusplayer_handle espp;
+       struct {
+               GThread *thread;
+               GAsyncQueue *queue;
+       } snapshot;
 } tb_data_s;
 
+typedef struct {
+       int32_t id;
+       uint32_t width;
+       uint32_t height;
+       uint32_t size;
+       unsigned char *data;
+} snapshot_data_s;
+
 static void __handle_init_event(handler_userdata_s *hdata, espp_service_data_from_client_s *data, espp_service_data_from_server_s *result)
 {
        int ret;
@@ -55,6 +77,59 @@ static void __handle_init_event(handler_userdata_s *hdata, espp_service_data_fro
        result->ret = 0;
 }
 
+typedef struct {
+       int cb_id;
+       bool exit;
+} queue_data_s;
+
+static void __release_qd(gpointer data)
+{
+       queue_data_s *qd = (queue_data_s *)data;
+
+       LOG_DEBUG("release qd[%p, exit:%d] done", qd, qd->exit);
+       g_free(qd);
+}
+
+static int __init_snapshot_thread(tb_data_s *tbs)
+{
+       ASSERT(tbs);
+       ASSERT(!tbs->snapshot.queue);
+       ASSERT(!tbs->snapshot.thread);
+
+       tbs->snapshot.queue = g_async_queue_new_full(__release_qd);
+
+       if (!(tbs->snapshot.thread = g_thread_try_new("__snapshot_work_thread", __snapshot_work_thread, (gpointer)tbs, NULL))) {
+               LOG_ERROR("failed to g_thread_try_new()");
+               g_async_queue_unref(tbs->snapshot.queue);
+               tbs->snapshot.queue = NULL;
+               return -1;
+       }
+
+       return 0;
+}
+
+static void __deinit_snapshot_thread(tb_data_s *tbs)
+{
+       queue_data_s *qd;
+
+       ASSERT(tbs);
+       ASSERT(tbs->snapshot.queue);
+       ASSERT(tbs->snapshot.thread);
+
+       qd = g_new0(queue_data_s, 1);
+       qd->exit = true;
+       g_async_queue_push_front(tbs->snapshot.queue, qd);
+
+       LOG_DEBUG("waiting for thread join...");
+       g_thread_join(tbs->snapshot.thread);
+       LOG_DEBUG("snapshot thread exits");
+
+       g_async_queue_unref(tbs->snapshot.queue);
+
+       tbs->snapshot.queue = NULL;
+       tbs->snapshot.thread = NULL;
+}
+
 static void __handle_create(handler_userdata_s *hdata, espp_service_data_from_client_s *data, espp_service_data_from_server_s *result)
 {
        int ret;
@@ -87,14 +162,21 @@ static void __handle_create(handler_userdata_s *hdata, espp_service_data_from_cl
                return;
        }
 
+       if (__init_snapshot_thread(tbs) != 0) {
+               g_hash_table_remove(hdata->svc->fd_table, hdata->key);
+               esplusplayer_destroy(espp);
+               return;
+       }
+
        result->ret = 0;
 }
 
 static void __handle_destroy(handler_userdata_s *hdata, espp_service_data_from_client_s *data, espp_service_data_from_server_s *result)
 {
+       tb_data_s *tbs;
        result->ret = -1;
 
-       RET_IF(!g_hash_table_lookup(hdata->svc->fd_table, hdata->key), "failed to g_hash_table_lookup(), key[%s]", hdata->key);
+       RET_IF(!(tbs = g_hash_table_lookup(hdata->svc->fd_table, hdata->key)), "failed to g_hash_table_lookup(), key[%s]", hdata->key);
 
        RET_IF(esplusplayer_destroy((esplusplayer_handle)hdata->espp) != ESPLUSPLAYER_ERROR_TYPE_NONE, "failed to esplusplayer_destroy()");
 
@@ -102,6 +184,8 @@ static void __handle_destroy(handler_userdata_s *hdata, espp_service_data_from_c
 
        ASSERT(g_hash_table_steal(hdata->svc->fd_table, hdata->key));
 
+       __deinit_snapshot_thread(tbs);
+
        result->ret = 0;
 }
 
@@ -660,6 +744,259 @@ static void __handle_set_decoded_video_frame_buffer_type(handler_userdata_s *hda
        result->ret = 0;
 }
 
+static int __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, snapshot_data_s *result)
+{
+       int ret;
+       mm_util_image_h src_image;
+       mm_util_image_h dst_image;
+       size_t size;
+
+       RET_VAL_IF(src_data == NULL, -1, "src_data is NULL");
+       RET_VAL_IF(result == NULL, -1, "result is NULL");
+
+       ret = mm_image_create_image(src_w, src_h, src_fmt, src_data, src_size, &src_image);
+       RET_VAL_IF(ret != MM_ERROR_NONE, -1, "failed to mm_image_create_image()");
+
+       ret = mm_util_convert_colorspace(src_image, dst_fmt, &dst_image);
+       mm_image_destroy_image(src_image);
+       RET_VAL_IF(ret != MM_ERROR_NONE, -1, "failed to mm_util_convert_colorspace()");
+
+       mm_image_debug_image(dst_image, NULL);
+
+       ret = mm_image_get_image(dst_image, &result->width, &result->height, NULL, &result->data, &size);
+       mm_image_destroy_image(dst_image);
+       RET_VAL_IF(ret != MM_ERROR_NONE, -1, "failed to mm_image_get_image()");
+
+       result->size = (uint32_t)size;
+
+       LOG_INFO("src[data:%p, size:%zu, %dx%d, fmt:%d] -> dst[data:%p, size:%u, %ux%u, fmt:%d]",
+               src_data, src_size, src_w, src_h, src_fmt, result->data, result->size, result->width, result->height, dst_fmt);
+
+       return 0;
+}
+
+static int __get_mm_util_color_format(tbm_format in_format, mm_util_color_format_e *out_format)
+{
+       switch(in_format) {
+               case TBM_FORMAT_NV12:
+                       *out_format = MM_UTIL_COLOR_NV12;
+                       break;
+               case TBM_FORMAT_YUV420:
+                       *out_format = MM_UTIL_COLOR_YUV420;
+                       break;
+               case TBM_FORMAT_BGRA8888:
+                       *out_format = MM_UTIL_COLOR_BGRA;
+                       break;
+               case TBM_FORMAT_BGRX8888:
+                       *out_format = MM_UTIL_COLOR_BGRX;
+                       break;
+               case TBM_FORMAT_ARGB8888:
+                       *out_format = MM_UTIL_COLOR_ARGB;
+                       break;
+               default:
+                       LOG_ERROR("invalid format");
+                       return -1;
+       }
+       return 0;
+}
+
+static int __convert_colorspace_to_rgb24(tbm_surface_h tbm_surf, snapshot_data_s *snapshot)
+{
+       guint src_size;
+       unsigned char *dst_buffer = NULL;
+       unsigned char *tmp_dst = NULL;
+       unsigned char *tmp_src = NULL;
+       unsigned int i;
+       tbm_surface_info_s info;
+       mm_util_color_format_e src_format;
+
+       RET_VAL_IF(!tbm_surf, -1, "tbm_surf is NULL");
+       RET_VAL_IF(!snapshot, -1, "snapshot is NULL");
+
+       if (tbm_surface_get_info(tbm_surf, &info)) {
+         LOG_ERROR("failed to tbm_surface_get_info()");
+         return -1;
+       }
+
+       LOG_DEBUG("%c%c%c%c, %dx%d, size:%d, num of planes:%d",
+               FOURCC_STR(info.format), info.width, info.height, info.size, info.num_planes);
+
+       if (__get_mm_util_color_format(info.format, &src_format) < 0) {
+               LOG_ERROR("failed to __get_mm_util_color_format()");
+               return -1;
+       }
+
+       switch (src_format) {
+       case MM_UTIL_COLOR_NV12:
+               src_size = (info.width * info.height) + (info.width * (info.height >> 1));
+
+               dst_buffer = (unsigned char *)g_malloc(src_size);
+               tmp_dst = dst_buffer;
+
+               /* Y plane */
+               tmp_src = (unsigned char *)info.planes[0].ptr;
+               for (i = 0; i < info.height; i++) {
+                       memcpy(tmp_dst, tmp_src, info.width);
+                       tmp_dst += info.width;
+                       tmp_src += info.planes[0].stride;
+               }
+               /* UV plane*/
+               tmp_src = (unsigned char *)info.planes[1].ptr;
+               for (i = 0; i < (info.height >> 1); i++) {
+                       memcpy(tmp_dst, tmp_src, info.width);
+                       tmp_dst += info.width;
+                       tmp_src += info.planes[1].stride;
+               }
+               break;
+       case MM_UTIL_COLOR_YUV420:
+               src_size = (info.width * info.height) * 2;
+
+               dst_buffer = (unsigned char *)g_malloc(src_size);
+               tmp_dst = dst_buffer;
+
+               /* Y plane */
+               tmp_src = (unsigned char *)info.planes[0].ptr;
+               for (i = 0; i < info.height; i++) {
+                       memcpy(tmp_dst, tmp_src, info.width);
+                       tmp_dst += info.width;
+                       tmp_src += info.planes[0].stride;
+               }
+               /* U plane */
+               tmp_src = (unsigned char *)info.planes[1].ptr;
+               for (i = 0; i < (info.height >> 1); i++) {
+                       memcpy(tmp_dst, tmp_src, (info.width >> 1));
+                       tmp_dst += (info.width >> 1);
+                       tmp_src += info.planes[1].stride;
+               }
+               /* V plane */
+               tmp_src = (unsigned char *)info.planes[2].ptr;
+               for (i = 0; i < (info.height >> 1); i++) {
+                       memcpy(tmp_dst, tmp_src, (info.width >> 1));
+                       tmp_dst += (info.width >> 1);
+                       tmp_src += info.planes[2].stride;
+               }
+               break;
+       case MM_UTIL_COLOR_BGRX:
+       case MM_UTIL_COLOR_BGRA:
+       case MM_UTIL_COLOR_ARGB:
+               src_size = (info.width * 4) * info.height;
+
+               dst_buffer = (unsigned char *)g_malloc(src_size);
+               memcpy(dst_buffer, info.planes[0].ptr, src_size);
+               break;
+       default:
+               LOG_ERROR("not supported format");
+               return -1;
+       }
+
+       return __convert_colorspace(dst_buffer, src_size, info.width, info.height,
+               src_format, MM_UTIL_COLOR_RGB24, snapshot);
+}
+
+static int __get_rgb24_frame(esplusplayer_handle espp, snapshot_data_s *snapshot)
+{
+       int ret;
+       esplusplayer_decoded_video_packet pkt;
+       esplusplayer_get_decoded_video_frame_status_type status;
+       gint64 start_time = g_get_monotonic_time();
+
+       ASSERT(espp);
+       ASSERT(snapshot);
+
+       ret = esplusplayer_get_decoded_video_packet(espp, &pkt, &status);
+       RET_VAL_IF(ret != ESPLUSPLAYER_ERROR_TYPE_NONE, -1, "failed to esplusplayer_get_decoded_video_packet(), ESPP[%p]", espp);
+       RET_VAL_IF(status != ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_SUCCESS, -1, "failed to esplusplayer_get_decoded_video_packet(), ESPP[%p], status[%d]", espp, status);
+       LOG_DEBUG("elapsed: %"G_GINT64_FORMAT" ms (esplusplayer_get_decoded_video_packet())",
+               USECONDS_TO_MSECONDS(g_get_monotonic_time() - start_time));
+
+       if (__convert_colorspace_to_rgb24((tbm_surface_h)pkt.surface_data, snapshot) < 0)
+               goto exit;
+       LOG_DEBUG("elapsed: %"G_GINT64_FORMAT" ms (__convert_colorspace_to_rgb24())",
+               USECONDS_TO_MSECONDS(g_get_monotonic_time() - start_time));
+
+       LOG_INFO("ESPP[%p], snapshot[%ux%u, size:%u, data:%p]",
+               espp, snapshot->width, snapshot->height, snapshot->size, snapshot->data);
+exit:
+       if (esplusplayer_decoded_buffer_destroy(espp, &pkt) != ESPLUSPLAYER_ERROR_TYPE_NONE)
+               LOG_WARNING("failed to esplusplayer_decoded_buffer_destroy()");
+
+       return 0;
+}
+
+static void __snapshot_cb(tb_data_s *tbs, snapshot_data_s *snapshot)
+{
+       espp_service_data_from_server_s data = {0, };
+
+       ASSERT(tbs);
+       ASSERT(snapshot);
+
+       LOG_DEBUG("event_fd[%d], ESPP[%p]", tbs->event_fd, tbs->espp);
+
+       FILL_SOCKET_MSG_EVENT(data, ESPP_SERVICE_EVENT_CB_SNAPSHOT);
+       FILL_SOCKET_MSG_PARAMS(data, ESPP_SERVICE_EVENT_CB_SNAPSHOT,
+               "id", snapshot->id, "width", snapshot->width, "height", snapshot->height, "size", snapshot->size);
+
+       espp_service_send_data(tbs->event_fd, &data);
+       espp_service_send_buffer(tbs->event_fd, snapshot->data, snapshot->size);
+}
+
+static gpointer __snapshot_work_thread(gpointer data)
+{
+       tb_data_s *tbs = (tb_data_s *)data;
+       queue_data_s *qd;
+
+       while (1) {
+               snapshot_data_s snapshot = {-1, 0, 0, 0, NULL};
+
+               LOG_DEBUG("wait for data...");
+               if (!(qd = g_async_queue_pop(tbs->snapshot.queue))) {
+                       LOG_ERROR("qd is NULL");
+                       break;
+               }
+               LOG_INFO("process qd[%p, cb_id:%d, exit:%d]", qd, qd->cb_id, qd->exit);
+               if (qd->exit) {
+                       __release_qd(qd);
+                       break;
+               }
+
+               snapshot.id = qd->cb_id;
+               if (__get_rgb24_frame(tbs->espp, &snapshot) == 0) {
+                       __snapshot_cb(tbs, &snapshot);
+                       g_free(snapshot.data);
+               }
+               __release_qd(qd);
+       }
+
+       LOG_DEBUG("exit");
+       return NULL;
+}
+
+static void __handle_take_snapshot(handler_userdata_s *hdata, espp_service_data_from_client_s *data, espp_service_data_from_server_s *result)
+{
+       int ret;
+       int id;
+       tb_data_s *tbs;
+       queue_data_s *qd;
+
+       result->ret = -1;
+
+       RET_IF(!(tbs = g_hash_table_lookup(hdata->svc->fd_table, hdata->key)), "failed to g_hash_table_lookup(), key[%s]", hdata->key);
+
+       ASSERT(tbs->snapshot.queue);
+
+       ret = espp_service_msg_parse_params(data->params, data->request, &id);
+       if (ret != 0)
+               return;
+
+       qd = g_new0(queue_data_s, 1);
+       qd->cb_id = id;
+       g_async_queue_push(tbs->snapshot.queue, qd);
+
+       LOG_INFO("qd[%p, cb_id:%d]", qd, id);
+
+       result->ret = 0;
+}
+
 static void __ready_to_prepare_cb(const int type, void *user_data)
 {
        handler_userdata_s *hdata = (handler_userdata_s *)user_data;
@@ -894,6 +1231,7 @@ static func_handler handlers[] = {
        [ESPP_SERVICE_REQUEST_SET_BUFFER_SIZE] = __handle_set_buffer_size,
        [ESPP_SERVICE_REQUEST_SET_LOW_LATENCY_MODE] = __handle_set_low_latency_mode,
        [ESPP_SERVICE_REQUEST_SET_DECODED_VIDEO_FRAME_BUFFER_TYPE] = __handle_set_decoded_video_frame_buffer_type,
+       [ESPP_SERVICE_REQUEST_TAKE_SNAPSHOT] = __handle_take_snapshot,
        [ESPP_SERVICE_REQUEST_SET_CALLBACK] = __handle_set_callback,
 };
 
index 60c5b2bdcba15343294c32f9fc5d3b5cece86e28..cbf267f504f0d88199c1445b969d30e7b967d90d 100644 (file)
@@ -72,6 +72,7 @@ int espp_service_init_socket(espp_service_s *svc);
 void espp_service_deinit_socket(espp_service_s *svc);
 int espp_service_send_data(int fd, espp_service_data_from_server_s *data);
 int espp_service_read_buffer(int fd, char *buffer, uint32_t size);
+int espp_service_send_buffer(int fd, const unsigned char *buffer, uint32_t size);
 
 /* handler */
 int espp_service_func_handler(handler_userdata_s *hdata, espp_service_data_from_client_s *data, espp_service_data_from_server_s *result);
index df7a05d90300f0d08e7147e964adfdbe14c9a964..e6b240401ddcdb40c42bf8bd86a218ae2ab1a12b 100644 (file)
@@ -364,6 +364,25 @@ int espp_service_send_data(int fd, espp_service_data_from_server_s *data)
        return 0;
 }
 
+int espp_service_send_buffer(int fd, const unsigned char *buffer, uint32_t size)
+{
+       char str_error[MAX_ERROR_LEN] = {'\0',};
+
+       ASSERT(fd >= 0);
+       ASSERT(buffer);
+       ASSERT(size > 0);
+
+       if (write(fd, buffer, size) < 0) {
+               strerror_r(errno, str_error, sizeof(str_error));
+               LOG_ERROR("failed to write(), fd[%d], err: %s", fd, str_error);
+               return -1;
+       }
+
+       LOG_DEBUG("fd[%d] buffer[%p] size[%u]", fd, buffer, size);
+
+       return 0;
+}
+
 int espp_service_read_buffer(int fd, char *buffer, uint32_t size)
 {
        int ret;
index 5d8564e632ea5f5a0ab36faa918b3dd155e5e760..59597382b73e3b96f7941ef123b7ae93aad86247 100644 (file)
@@ -21,7 +21,12 @@ message('================================================')
 
 thread_dep = dependency('threads', required: true)
 espp_dep = dependency('esplusplayer', required: true)
-daemon_deps += [thread_dep, espp_dep]
+tbm_dep = dependency('libtbm', required: true)
+mm_common_dep = dependency('mm-common', required: true)
+mm_util_dep = dependency('mmutil-common', required: true)
+mmutil_imgp_dep = dependency('mmutil-imgp', required: true)
+
+daemon_deps += [thread_dep, espp_dep, tbm_dep, mm_common_dep, mm_util_dep, mmutil_imgp_dep]
 
 executable('espp-service',
   espp_service_sources,
index 3d8b09c05a06dc68b5f04c48cfbfbda236616f22..d6afad924a3b701a55265e1ee4f576b02000d446 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<manifest xmlns="http://tizen.org/ns/packages" api-version="7.0" package="com.samsung.tizen.espp-service" version="0.3.11">
+<manifest xmlns="http://tizen.org/ns/packages" api-version="7.0" package="com.samsung.tizen.espp-service" version="0.3.12">
     <profile name="mobile"/>
     <description>espp-service</description>
     <service-application appid="com.samsung.tizen.espp-service" auto-restart="false" exec="espp-service" multiple="false" nodisplay="false" on-boot="false" taskmanage="false" type="capp">