[ACR-1589] Add new APIs for video encode decision 32/240432/14 submit/tizen/20200818.102105 submit/tizen/20200819.022508
authorJeongmo Yang <jm80.yang@samsung.com>
Thu, 6 Aug 2020 08:00:26 +0000 (17:00 +0900)
committerJeongmo Yang <jm80.yang@samsung.com>
Fri, 14 Aug 2020 06:59:47 +0000 (15:59 +0900)
[Version] 0.3.24
[Issue Type] New feature

Change-Id: Icea6968749d67b59b99b89ff1c5c2c4757d91993
Signed-off-by: Jeongmo Yang <jm80.yang@samsung.com>
include/recorder.h
include/recorder_private.h
packaging/capi-media-recorder.spec
src/recorder.c
test/recorder_test.c

index 083cdea..919d013 100644 (file)
@@ -174,6 +174,12 @@ typedef enum {
 } recorder_device_state_e;
 
 /**
+ * @brief The structure type to contain video stream data.
+ * @since_tizen 6.0
+ */
+typedef camera_preview_data_s recorder_video_data_s;
+
+/**
  * @}
 */
 
@@ -292,6 +298,20 @@ typedef void (*recorder_audio_stream_cb)(void *stream, int size, audio_sample_ty
 typedef void (*recorder_muxed_stream_cb)(void *stream, int size, unsigned long long offset, void *user_data);
 
 /**
+ * @brief Called when each video frame is delivered before encoding, \n
+ *        and it will be encoded if the application returns @c true, otherwise dropped.
+ * @since_tizen 6.0
+ *
+ * @remarks This function is issued in the context of internal framework so the UI update code should not be directly invoked.
+ * @remarks The @a frame should not be released and it's available until the callback returns.
+ *
+ * @param[in] frame     The reference pointer to video stream data
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see recorder_set_video_encode_decision_cb()
+ */
+typedef bool (*recorder_video_encode_decision_cb)(recorder_video_data_s *frame, void *user_data);
+
+/**
  * @brief Called once for each supported video resolution.
  * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
  * @param[in] width     The video image width
@@ -1149,6 +1169,42 @@ int recorder_set_muxed_stream_cb(recorder_h recorder, recorder_muxed_stream_cb c
 int recorder_unset_muxed_stream_cb(recorder_h recorder);
 
 /**
+ * @brief Registers a callback function to be called when each video frame is delivered before encoding.
+ * @since_tizen 6.0
+ * @remarks The audio stream will be disabled by force if @a callback is set.
+ * @param[in] recorder  The handle to the recorder
+ * @param[in] callback  The callback function to register
+ * @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 #RECORDER_ERROR_NONE Successful
+ * @retval #RECORDER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #RECORDER_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #RECORDER_ERROR_INVALID_STATE Invalid state
+ * @retval #RECORDER_ERROR_NOT_SUPPORTED The feature is not supported
+ * @pre The recorder state should be #RECORDER_STATE_READY or #RECORDER_STATE_CREATED.
+ * @post The @a callback will be invoked when each video frame is delivered before encoding, \n
+ *       and it will be encoded if the @a callback returns @c true, otherwise dropped.
+ * @see recorder_unset_video_encode_decision_cb()
+ * @see recorder_video_encode_decision_cb()
+ */
+int recorder_set_video_encode_decision_cb(recorder_h recorder, recorder_video_encode_decision_cb callback, void *user_data);
+
+/**
+ * @brief Unregisters the callback function.
+ * @since_tizen 6.0
+ * @param[in] recorder The handle to the media recorder
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #RECORDER_ERROR_NONE Successful
+ * @retval #RECORDER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #RECORDER_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #RECORDER_ERROR_INVALID_STATE Invalid state
+ * @retval #RECORDER_ERROR_NOT_SUPPORTED The feature is not supported
+ * @pre The recorder state should be #RECORDER_STATE_READY or #RECORDER_STATE_CREATED.
+ * @see recorder_set_video_encode_decision_cb()
+ */
+int recorder_unset_video_encode_decision_cb(recorder_h recorder);
+
+/**
  * @brief Registers a callback function to be invoked when the recording information changes.
  * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
  * @param[in] recorder  The handle to the media recorder
index fe80627..c5e9418 100644 (file)
@@ -123,7 +123,7 @@ typedef struct _recorder_message_s {
        muse_recorder_api_e api;
        muse_recorder_event_e event;
        muse_recorder_event_class_e event_class;
-       int tfd;
+       int tfd[MUSE_NUM_FD];
 } recorder_message_s;
 
 typedef struct _recorder_idle_event_s {
index 31f0cff..c8e8fae 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-recorder
 Summary:    A Recorder API
-Version:    0.3.23
+Version:    0.3.24
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index d900a7b..a462bf4 100644 (file)
@@ -164,7 +164,116 @@ static void _recorder_release_imported_bo(tbm_bo *bo)
        return;
 }
 
-static void _recorder_client_user_callback(recorder_cb_info_s *cb_info, char *recv_msg, muse_recorder_event_e event, int tfd)
+static void _recorder_event_handler_video_encode_decision(recorder_cb_info_s *cb_info, char *recv_msg, int *tfd)
+{
+       int i = 0;
+       int event = MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION;
+       int video_fd = -1;
+       int num_buffer_fd = 0;
+       bool do_encode = true;
+
+       tbm_bo bo = NULL;
+       tbm_bo buffer_bo[BUFFER_MAX_PLANE_NUM] = {NULL, };
+       tbm_bo_handle bo_handle = {.ptr = NULL};
+       tbm_bo_handle buffer_bo_handle[BUFFER_MAX_PLANE_NUM] = {{.ptr = NULL}, };
+       tbm_bo data_bo = NULL;
+       tbm_bo_handle data_bo_handle = {.ptr = NULL};
+
+       recorder_video_data_s frame;
+       camera_stream_data_s *stream = NULL;
+
+       char *send_msg = NULL;
+
+       /* tfd[0]: MMCamcorderVideoStreamDataType
+          tfd[1]: data_bo or zero copy bo[0]
+          tfd[2]: zero copy bo[1]
+          tfd[3]: zero copy bo[2] */
+       if (!tfd || tfd[0] < 0) {
+               LOGE("invalid fd %d", tfd ? tfd[0] : 0);
+               return;
+       }
+
+       muse_recorder_msg_get(video_fd, recv_msg);
+       muse_recorder_msg_get(num_buffer_fd, recv_msg);
+
+       /*LOGD("video_fd %d, num_buffer_fd %d", video_fd, num_buffer_fd);*/
+
+       memset(&frame, 0x0, sizeof(recorder_video_data_s));
+
+       if (num_buffer_fd < 0 || num_buffer_fd > BUFFER_MAX_PLANE_NUM) {
+               LOGE("invalid num buffer fd %d", num_buffer_fd);
+               goto _VIDEO_ENCODE_DECISION_CB_HANDLER_DONE;
+       }
+
+       /* import tbm bo and get virtual address */
+       if (!_recorder_import_tbm_fd(cb_info->bufmgr, tfd[0], &bo, &bo_handle)) {
+               LOGE("failed to import fd %d", tfd[0]);
+               goto _VIDEO_ENCODE_DECISION_CB_HANDLER_DONE;
+       }
+
+       if (num_buffer_fd == 0 && tfd[1] >= 0) {
+               /* import tbm data_bo and get virtual address */
+               if (!_recorder_import_tbm_fd(cb_info->bufmgr, tfd[1], &data_bo, &data_bo_handle)) {
+                       LOGE("failed to import data fd %d", tfd[1]);
+                       goto _VIDEO_ENCODE_DECISION_CB_HANDLER_DONE;
+               }
+       }
+
+       /* get stream info */
+       stream = (camera_stream_data_s *)bo_handle.ptr;
+
+       for (i = 0 ; i < num_buffer_fd ; i++) {
+               /* import buffer bo and get virtual address */
+               if (!_recorder_import_tbm_fd(cb_info->bufmgr, tfd[i + 1], &buffer_bo[i], &buffer_bo_handle[i])) {
+                       LOGE("failed to import buffer fd %d", tfd[i + 1]);
+                       goto _VIDEO_ENCODE_DECISION_CB_HANDLER_DONE;
+               }
+       }
+
+       if (cb_info->user_cb[event]) {
+               camera_create_preview_frame(stream, num_buffer_fd, buffer_bo_handle, &data_bo_handle, &frame);
+               do_encode = ((recorder_video_encode_decision_cb)cb_info->user_cb[event])(&frame, cb_info->user_data[event]);
+       }
+
+_VIDEO_ENCODE_DECISION_CB_HANDLER_DONE:
+       /* return buffer */
+       send_msg = muse_core_msg_new(MUSE_RECORDER_API_RETURN_BUFFER,
+               MUSE_TYPE_INT, "ret_fd", video_fd,
+               MUSE_TYPE_INT, "buffer_type", MUSE_RECORDER_BUFFER_TYPE_VIDEO_ENCODE_DECISION,
+               MUSE_TYPE_INT, "video_encode_decision", (int)do_encode,
+               NULL);
+       if (send_msg) {
+               if (muse_core_msg_send(cb_info->fd, send_msg) <= 0)
+                       LOGE("sending message failed");
+
+               muse_core_msg_free(send_msg);
+               send_msg = NULL;
+       } else {
+               LOGE("failed to create send msg for fd %d", tfd[0]);
+       }
+
+       /* release imported bo and fd */
+       for (i = 0 ; i < num_buffer_fd && i < BUFFER_MAX_PLANE_NUM ; i++)
+               _recorder_release_imported_bo(&buffer_bo[i]);
+
+       /* unmap and unref tbm bo */
+       if (data_bo)
+               _recorder_release_imported_bo(&data_bo);
+       if (bo)
+               _recorder_release_imported_bo(&bo);
+
+       /* close imported fd */
+       for (i = 0 ; i < MUSE_NUM_FD ; i++) {
+               if (tfd[i] >= 0) {
+                       close(tfd[i]);
+                       tfd[i] = -1;
+               } else {
+                       break;
+               }
+       }
+}
+
+static void _recorder_client_user_callback(recorder_cb_info_s *cb_info, char *recv_msg, muse_recorder_event_e event, int *tfd)
 {
        if (recv_msg == NULL || event >= MUSE_RECORDER_EVENT_TYPE_NUM) {
                LOGE("invalid parameter - recorder msg %p, event %d", recv_msg, event);
@@ -177,7 +286,8 @@ static void _recorder_client_user_callback(recorder_cb_info_s *cb_info, char *re
 
        if (cb_info->user_cb[event] == NULL) {
                if (event != MUSE_RECORDER_EVENT_TYPE_AUDIO_STREAM &&
-                       event != MUSE_RECORDER_EVENT_TYPE_MUXED_STREAM) {
+                       event != MUSE_RECORDER_EVENT_TYPE_MUXED_STREAM &&
+                       event != MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION) {
                        g_mutex_unlock(&cb_info->user_cb_mutex[event]);
                        LOGW("NULL callback for event %d, return here", event);
                        return;
@@ -269,15 +379,15 @@ static void _recorder_client_user_callback(recorder_cb_info_s *cb_info, char *re
                        tbm_bo_handle bo_handle = {.ptr = NULL};
                        char *send_msg = NULL;
 
-                       if (tfd < 0) {
-                               LOGE("invalid fd %d", tfd);
+                       if (tfd && tfd[0] < 0) {
+                               LOGE("invalid fd %d", tfd ? tfd[0] : 0);
                                break;
                        }
 
                        muse_recorder_msg_get(audio_fd, recv_msg);
 
                        if (cb_info->user_cb[event]) {
-                               if (_recorder_import_tbm_fd(cb_info->bufmgr, tfd, &bo, &bo_handle)) {
+                               if (_recorder_import_tbm_fd(cb_info->bufmgr, tfd[0], &bo, &bo_handle)) {
                                        muse_recorder_msg_get(size, recv_msg);
                                        muse_recorder_msg_get(format, recv_msg);
                                        muse_recorder_msg_get(channel, recv_msg);
@@ -295,14 +405,15 @@ static void _recorder_client_user_callback(recorder_cb_info_s *cb_info, char *re
                                        /* release imported bo */
                                        _recorder_release_imported_bo(&bo);
                                } else {
-                                       LOGE("tbm fd %d import failed", tfd);
+                                       LOGE("tbm fd %d import failed", tfd[0]);
                                }
                        }
 
                        /* return buffer */
                        send_msg = muse_core_msg_new(MUSE_RECORDER_API_RETURN_BUFFER,
-                               MUSE_TYPE_INT, "ret_fd", audio_fd, NULL);
-
+                               MUSE_TYPE_INT, "ret_fd", audio_fd,
+                               MUSE_TYPE_INT, "buffer_type", MUSE_RECORDER_BUFFER_TYPE_AUDIO_STREAM,
+                               NULL);
                        if (send_msg) {
                                if (muse_core_msg_send(cb_info->fd, send_msg) <= 0)
                                        LOGE("sending message failed");
@@ -314,8 +425,8 @@ static void _recorder_client_user_callback(recorder_cb_info_s *cb_info, char *re
                        }
 
                        /* close imported fd */
-                       close(tfd);
-                       tfd = -1;
+                       close(tfd[0]);
+                       tfd[0] = -1;
                        break;
                }
        case MUSE_RECORDER_EVENT_TYPE_MUXED_STREAM:
@@ -328,15 +439,15 @@ static void _recorder_client_user_callback(recorder_cb_info_s *cb_info, char *re
                        tbm_bo_handle bo_handle = {.ptr = NULL};
                        char *send_msg = NULL;
 
-                       if (tfd < 0) {
-                               LOGE("invalid fd %d", tfd);
+                       if (tfd && tfd[0] < 0) {
+                               LOGE("invalid fd %d", tfd ? tfd[0] : 0);
                                break;
                        }
 
                        muse_recorder_msg_get(muxed_fd, recv_msg);
 
                        if (cb_info->user_cb[event]) {
-                               if (_recorder_import_tbm_fd(cb_info->bufmgr, tfd, &bo, &bo_handle)) {
+                               if (_recorder_import_tbm_fd(cb_info->bufmgr, tfd[0], &bo, &bo_handle)) {
                                        muse_recorder_msg_get(size, recv_msg);
                                        muse_recorder_msg_get_int64(offset, recv_msg);
 
@@ -350,13 +461,15 @@ static void _recorder_client_user_callback(recorder_cb_info_s *cb_info, char *re
                                        /* release imported bo */
                                        _recorder_release_imported_bo(&bo);
                                } else {
-                                       LOGE("tbm fd %d import failed", tfd);
+                                       LOGE("tbm fd %d import failed", tfd[0]);
                                }
                        }
 
                        /* return buffer */
                        send_msg = muse_core_msg_new(MUSE_RECORDER_API_RETURN_BUFFER,
-                               MUSE_TYPE_INT, "ret_fd", muxed_fd, NULL);
+                               MUSE_TYPE_INT, "ret_fd", muxed_fd,
+                               MUSE_TYPE_INT, "buffer_type", MUSE_RECORDER_BUFFER_TYPE_MUXED_STREAM,
+                               NULL);
                        if (send_msg) {
                                if (muse_core_msg_send(cb_info->fd, send_msg) <= 0)
                                        LOGE("sending message failed");
@@ -364,14 +477,17 @@ static void _recorder_client_user_callback(recorder_cb_info_s *cb_info, char *re
                                muse_core_msg_free(send_msg);
                                send_msg = NULL;
                        } else {
-                               LOGE("failed to create send msg for fd %d", tfd);
+                               LOGE("failed to create send msg for fd %d", tfd[0]);
                        }
 
                        /* close imported fd */
-                       close(tfd);
-                       tfd = -1;
+                       close(tfd[0]);
+                       tfd[0] = -1;
                        break;
                }
+       case MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION:
+               _recorder_event_handler_video_encode_decision(cb_info, recv_msg, tfd);
+               break;
 //LCOV_EXCL_START
        case MUSE_RECORDER_EVENT_TYPE_ERROR:
                {
@@ -476,7 +592,7 @@ static gboolean _recorder_idle_event_callback(gpointer data)
        g_mutex_unlock(&g_rec_idle_event_lock);
 
        /* user callback */
-       _recorder_client_user_callback(cb_info, rec_idle_event->recv_msg, rec_idle_event->event, -1);
+       _recorder_client_user_callback(cb_info, rec_idle_event->recv_msg, rec_idle_event->event, NULL);
 
 IDLE_EVENT_CALLBACK_DONE:
        /* release event */
@@ -542,7 +658,7 @@ static void _recorder_deactivate_idle_event_all(recorder_cb_info_s *cb_info)
 }
 
 
-static void __recorder_add_msg_to_queue(recorder_cb_info_s *cb_info, int api, int event, int event_class, char *msg, int tfd)
+static void __recorder_add_msg_to_queue(recorder_cb_info_s *cb_info, int api, int event, int event_class, char *msg, int *tfd)
 {
        recorder_message_s *rec_msg = NULL;
        recorder_msg_handler_info_s *msg_handler_info = NULL;
@@ -561,7 +677,9 @@ static void __recorder_add_msg_to_queue(recorder_cb_info_s *cb_info, int api, in
        rec_msg->api = api;
        rec_msg->event = event;
        rec_msg->event_class = event_class;
-       rec_msg->tfd = tfd;
+
+       if (tfd && tfd[0] >= 0)
+               memcpy(rec_msg->tfd, tfd, sizeof(rec_msg->tfd));
 
        strncpy(rec_msg->recv_msg, msg, sizeof(rec_msg->recv_msg) - 1);
 
@@ -720,7 +838,7 @@ static void __recorder_get_api_operation(int api, recorder_cb_info_s *cb_info, c
 }
 
 
-static void __recorder_process_msg(recorder_cb_info_s *cb_info, char *msg, int tfd)
+static void __recorder_process_msg(recorder_cb_info_s *cb_info, char *msg, int *tfd)
 {
        int ret = RECORDER_ERROR_NONE;
        int api = -1;
@@ -937,6 +1055,7 @@ static void *_recorder_msg_handler_func(gpointer data)
 
 static void *_recorder_msg_recv_func(gpointer data)
 {
+       int i = 0;
        int recv_length = 0;
        int tfd[MUSE_NUM_FD] = {-1, -1, -1, -1};
        char *recv_msg = NULL;
@@ -952,8 +1071,8 @@ static void *_recorder_msg_recv_func(gpointer data)
        recv_msg = cb_info->recv_msg;
 
        while (g_atomic_int_get(&cb_info->msg_recv_running)) {
-               /* tfd[0] is only used. */
-               tfd[0] = -1;
+               for (i = 0 ; i < MUSE_NUM_FD ; i++)
+                       tfd[i] = -1;
 
                recv_length = muse_core_msg_recv_fd(cb_info->fd, recv_msg, MUSE_MSG_MAX_LENGTH, tfd);
                if (recv_length <= 0) {
@@ -968,7 +1087,7 @@ static void *_recorder_msg_recv_func(gpointer data)
 
                /*LOGD("recv msg : %s, length : %d", recv_msg, recv_length);*/
 
-               __recorder_process_msg(cb_info, recv_msg, tfd[0]);
+               __recorder_process_msg(cb_info, recv_msg, tfd);
        }
 
        LOGD("client cb exit - server connected %d", cb_info->is_server_connected);
@@ -993,7 +1112,7 @@ static void *_recorder_msg_recv_func(gpointer data)
                        MUSE_RECORDER_EVENT_TYPE_ERROR,
                        MUSE_RECORDER_EVENT_CLASS_THREAD_MAIN,
                        error_msg,
-                       -1);
+                       NULL);
 
                muse_core_msg_free(error_msg);
                error_msg = NULL;
@@ -2514,6 +2633,66 @@ int recorder_unset_muxed_stream_cb(recorder_h recorder)
 }
 
 
+int recorder_set_video_encode_decision_cb(recorder_h recorder, recorder_video_encode_decision_cb callback, void *user_data)
+{
+       int ret = RECORDER_ERROR_NONE;
+       recorder_cli_s *pc = (recorder_cli_s *)recorder;
+       muse_recorder_api_e api = MUSE_RECORDER_API_SET_VIDEO_ENCODE_DECISION_CB;
+
+       if (!pc || !pc->cb_info || !callback) {
+               LOGE("INVALID_PARAMETER(0x%08x)", RECORDER_ERROR_INVALID_PARAMETER);
+               return RECORDER_ERROR_INVALID_PARAMETER;
+       }
+
+       LOGD("Enter, handle :%td", pc->remote_handle);
+
+       _recorder_msg_send(api, pc->cb_info, &ret, RECORDER_CB_TIMEOUT);
+
+       if (ret == RECORDER_ERROR_NONE) {
+               g_mutex_lock(&pc->cb_info->user_cb_mutex[MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION]);
+
+               pc->cb_info->user_cb[MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION] = callback;
+               pc->cb_info->user_data[MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION] = user_data;
+
+               g_mutex_unlock(&pc->cb_info->user_cb_mutex[MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION]);
+       }
+
+       LOGD("ret : 0x%x", ret);
+
+       return ret;
+}
+
+
+int recorder_unset_video_encode_decision_cb(recorder_h recorder)
+{
+       int ret = RECORDER_ERROR_NONE;
+       muse_recorder_api_e api = MUSE_RECORDER_API_UNSET_VIDEO_ENCODE_DECISION_CB;
+       recorder_cli_s *pc = (recorder_cli_s *)recorder;
+
+       if (!pc || !pc->cb_info) {
+               LOGE("NULL handle");
+               return RECORDER_ERROR_INVALID_PARAMETER;
+       }
+
+       LOGD("ENTER");
+
+       _recorder_msg_send(api, pc->cb_info, &ret, RECORDER_CB_TIMEOUT);
+
+       if (ret == RECORDER_ERROR_NONE) {
+               g_mutex_lock(&pc->cb_info->user_cb_mutex[MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION]);
+
+               pc->cb_info->user_cb[MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION] = NULL;
+               pc->cb_info->user_data[MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION] = NULL;
+
+               g_mutex_unlock(&pc->cb_info->user_cb_mutex[MUSE_RECORDER_EVENT_TYPE_VIDEO_ENCODE_DECISION]);
+       }
+
+       LOGD("ret : 0x%x", ret);
+
+       return ret;
+}
+
+
 int recorder_set_error_cb(recorder_h recorder, recorder_error_cb callback, void *user_data)
 {
        int ret = RECORDER_ERROR_NONE;
index 13cf565..05d1908 100644 (file)
@@ -75,6 +75,7 @@ int recorder_state;
 int multishot_num;
 static GTimer *timer = NULL;
 static int g_recorder_device_changed_cb_id;
+static int ve_frame_count = 0;
 
 /*-----------------------------------------------------------------------
   |    GLOBAL CONSTANT DEFINITIONS:                                       |
@@ -97,10 +98,11 @@ static int g_recorder_device_changed_cb_id;
 #define SRC_VIDEO_FRAME_RATE_15         15    /* video input frame rate */
 #define SRC_VIDEO_FRAME_RATE_30         30    /* video input frame rate */
 
-#define TARGET_FILENAME_PATH            "/opt/usr/home/owner/media/Sounds/"
+#define TARGET_FILENAME_PATH            "/opt/usr/home/owner/media/"
 #define TARGET_FILENAME_VIDEO           TARGET_FILENAME_PATH"test_rec_video.mp4"
 #define TARGET_FILENAME_AUDIO           TARGET_FILENAME_PATH"test_rec_audio.m4a"
 #define TARGET_FILENAME_MUXED_CB        TARGET_FILENAME_PATH"muxed_stream_cb.mp4"
+#define TARGET_FILENAME_VE_DECISION_CB  TARGET_FILENAME_PATH"ve_decision_cb.data"
 
 #define AUDIO_SOURCE_SAMPLERATE_AAC     44100
 #define AUDIO_SOURCE_SAMPLERATE_AMR     8000
@@ -352,6 +354,62 @@ static void _recording_muxed_stream_cb(void *stream, int size, unsigned long lon
        return;
 }
 
+static bool _recording_video_encode_decision_cb(recorder_video_data_s *frame, void *user_data)
+{
+#if 0
+       FILE *fp = NULL;
+
+       fp = fopen(TARGET_FILENAME_VE_DECISION_CB, "a");
+       if (fp == NULL) {
+               g_print("\n==== file[%s] open failed ====\n", TARGET_FILENAME_VE_DECISION_CB);
+               return true;
+       }
+
+       if (frame->format == CAMERA_PIXEL_FORMAT_RGBA ||
+               frame->format == CAMERA_PIXEL_FORMAT_ARGB) {
+               fwrite(frame->data.rgb_plane.data, 1, frame->data.rgb_plane.size, fp);
+       } else if (frame->format == CAMERA_PIXEL_FORMAT_INVZ) {
+               fwrite(frame->data.depth_plane.data, 1, frame->data.depth_plane.size, fp);
+       } else if (frame->format == CAMERA_PIXEL_FORMAT_MJPEG) {
+               fwrite(frame->data.encoded_plane.data, 1, frame->data.encoded_plane.size, fp);
+       } else {
+               switch (frame->num_of_planes) {
+               case 1:
+                       fwrite(frame->data.single_plane.yuv, 1, frame->data.single_plane.size, fp);
+                       break;
+               case 2:
+                       fwrite(frame->data.double_plane.y, 1, frame->data.double_plane.y_size, fp);
+                       fwrite(frame->data.double_plane.uv, 1, frame->data.double_plane.uv_size, fp);
+                       break;
+               case 3:
+                       fwrite(frame->data.triple_plane.y, 1, frame->data.triple_plane.y_size, fp);
+                       fwrite(frame->data.triple_plane.u, 1, frame->data.triple_plane.u_size, fp);
+                       fwrite(frame->data.triple_plane.v, 1, frame->data.triple_plane.v_size, fp);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       fclose(fp);
+       fp = NULL;
+#endif
+
+       if (ve_frame_count == 0) {
+               g_print("==== VE decision cb : file[%s][fmt %d, %dx%d, plane %d] ====\n",
+                       TARGET_FILENAME_VE_DECISION_CB,
+                       frame->format, frame->width, frame->height, frame->num_of_planes);
+       }
+
+       if ((ve_frame_count++ % 10) >= 5) {
+               g_print("-------- VE decision cb : [ret:false]\n");
+               return false;
+       } else {
+               g_print("-------- VE decision cb : [ret:true ]\n");
+               return true;
+       }
+}
+
 
 static inline void flush_stdin()
 {
@@ -480,6 +538,7 @@ static void print_menu()
                        g_print("\t     'z' Video-stabilization \n");
                        g_print("\t     'm' Camcorder Motion Rate setting \n");
                        g_print("\t     'M' Set/Unset muxed stream callback \n");
+                       g_print("\t     'E' Set/Unset video encode decision callback \n");
                        g_print("\t     'F' Set file name - %s\n", TARGET_FILENAME_VIDEO);
                        g_print("\t     'b' back\n");
                        g_print("\t=======================================\n");
@@ -519,6 +578,7 @@ static void main_menu(gchar buf)
                        case '1': /* Start Recording */
                                g_print("*Recording start!\n");
                                hcamcorder->elapsed_time = 0;
+                               ve_frame_count = 0;
 
                                g_timer_reset(timer);
                                err = recorder_start(hcamcorder->recorder);
@@ -986,6 +1046,24 @@ static void setting_menu(gchar buf)
 
                        break;
 
+               case 'E': /* Setting > video encode decision callback */
+                       g_print("* Video encode decision callback\n");
+
+                       flush_stdin();
+
+                       g_print("[set(1)/unset(2)] : ");
+
+                       err = scanf("%d", &idx);
+
+                       if (idx == 1)
+                               result = recorder_set_video_encode_decision_cb(hcamcorder->recorder, _recording_video_encode_decision_cb, NULL);
+                       else if (idx == 2)
+                               result = recorder_unset_video_encode_decision_cb(hcamcorder->recorder);
+                       else
+                               result = RECORDER_ERROR_INVALID_PARAMETER;
+
+                       break;
+
                case 'F': /* Set file name */
                        result = recorder_set_filename(hcamcorder->recorder, TARGET_FILENAME_VIDEO);
                        break;