} 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;
+
+/**
* @}
*/
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
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
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);
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;
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);
/* 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");
}
/* close imported fd */
- close(tfd);
- tfd = -1;
+ close(tfd[0]);
+ tfd[0] = -1;
break;
}
case MUSE_RECORDER_EVENT_TYPE_MUXED_STREAM:
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);
/* 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");
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:
{
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 */
}
-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;
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);
}
-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;
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;
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) {
/*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);
MUSE_RECORDER_EVENT_TYPE_ERROR,
MUSE_RECORDER_EVENT_CLASS_THREAD_MAIN,
error_msg,
- -1);
+ NULL);
muse_core_msg_free(error_msg);
error_msg = NULL;
}
+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;
int multishot_num;
static GTimer *timer = NULL;
static int g_recorder_device_changed_cb_id;
+static int ve_frame_count = 0;
/*-----------------------------------------------------------------------
| GLOBAL CONSTANT DEFINITIONS: |
#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
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()
{
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");
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);
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;