Add new internal APIs for media packet internal callback 72/304872/3 accepted/tizen/7.0/unified/20240223.161513
authorJeongmo Yang <jm80.yang@samsung.com>
Thu, 25 Jan 2024 04:58:43 +0000 (13:58 +0900)
committerJeongmo Yang <jm80.yang@samsung.com>
Tue, 30 Jan 2024 12:26:22 +0000 (21:26 +0900)
- Added internal APIs
 : bool camera_is_supported_media_packet_preview_internal_cb(camera_h camera);
 : int camera_set_media_packet_preview_internal_cb(camera_h camera, camera_media_packet_preview_cb callback, void *user_data);
 : int camera_unset_media_packet_preview_internal_cb(camera_h camera);

[Version] 0.4.107
[Issue Type] New feature

Change-Id: I232c8a30f5638a59cf88c974acf3b812a414200e
Signed-off-by: Jeongmo Yang <jm80.yang@samsung.com>
include/camera_internal.h
include/camera_private.h
packaging/capi-media-camera.spec
src/camera.c
src/camera_internal.c
test/camera_test.c
test/camera_test.h

index 9a87bb0..22fbeba 100644 (file)
@@ -361,6 +361,57 @@ int camera_attr_get_preview_frame_status_auto_exposure(camera_h camera, camera_s
  */
 int camera_attr_get_preview_frame_status_auto_white_balance(camera_h camera, camera_status_auto_white_balance_e *status);
 
+/**
+ * @internal
+ * @brief Gets the media packet preview internal callback feature's supported state.
+ * @since_tizen 7.0
+ * @remarks The specific error code can be obtained using the get_last_result() method. Error codes are described in Exception section.
+ * @param[in] camera The handle to the camera
+ * @return @c true if supported, otherwise @c false
+ * @exception #CAMERA_ERROR_NONE Successful
+ * @exception #CAMERA_ERROR_NOT_SUPPORTED The feature is not supported
+ * @exception #CAMERA_ERROR_PERMISSION_DENIED The access to the resources can not be granted
+ * @exception #CAMERA_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+bool camera_is_supported_media_packet_preview_internal_cb(camera_h camera);
+
+/**
+ * @internal
+ * @brief Sets a media packet callback function to be invoked once per frame when previewing.
+ * @since_tizen 7.0
+ * @remarks A @a callback is invoked on the internal thread of the camera.\n
+ *          A video frame can be retrieved using a @a callback as a media packet.\n
+ *          The callback function holds the same buffer that will be drawn on the display device.\n
+ *          So if you change the media packet in a callback, it will be displayed on the device \n
+ *          and the media packet is available until it's released by media_packet_unref().
+ * @param[in] camera    The handle to the camera
+ * @param[in] callback  The callback function to be invoked
+ * @param[in] user_data The user data to be passed to the callback function
+ * @return 0 on success, otherwise a negative error value
+ * @retval #CAMERA_ERROR_NONE Successful
+ * @retval #CAMERA_ERROR_NOT_SUPPORTED The feature is not supported
+ * @retval #CAMERA_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #CAMERA_ERROR_SERVICE_DISCONNECTED The socket to multimedia server is disconnected
+ * @see camera_start_preview()
+ * @see camera_unset_media_packet_preview_internal_cb()
+ * @see camera_media_packet_preview_cb()
+ */
+int camera_set_media_packet_preview_internal_cb(camera_h camera, camera_media_packet_preview_cb callback, void *user_data);
+
+/**
+ * @internal
+ * @brief Unsets the media packet internal callback function.
+ * @since_tizen 7.0
+ * @param[in] camera The handle to the camera
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #CAMERA_ERROR_NONE Successful
+ * @retval #CAMERA_ERROR_NOT_SUPPORTED The feature is not supported
+ * @retval #CAMERA_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #CAMERA_ERROR_SERVICE_DISCONNECTED The socket to multimedia server is disconnected
+ * @see camera_set_media_packet_preview_internal_cb()
+ */
+int camera_unset_media_packet_preview_internal_cb(camera_h camera);
+
 
 /**
  * @}
index 343c2b0..ce04481 100644 (file)
@@ -297,6 +297,7 @@ void _camera_update_api_waiting(camera_cb_info_s *cb_info, int api, int value);
 void _camera_send_message_get_return(camera_cb_info_s *cb_info, muse_camera_api_e api, char *msg,
        int time_out, int *ret);
 int _camera_get_log_level(void);
+bool __is_supported(camera_h camera, muse_camera_api_e api);
 
 typedef bool (*camera_supported_cb_param1)(int param, void *user_data);
 typedef bool (*camera_supported_cb_param2)(int param1, int param2, void *user_data);
index bc53786..214ec9f 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-camera
 Summary:    A Camera API
-Version:    0.4.106
+Version:    0.4.107
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 9a7a0e5..6aa480f 100644 (file)
@@ -456,14 +456,14 @@ static void __camera_event_handler_preview(camera_cb_info_s *cb_info, char *recv
        }
 
        /* preview callback */
-       if (cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_PREVIEW] ||
-               cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_EXTRA_PREVIEW]) {
+       if ((cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_PREVIEW] && stream->extra_stream_id == MM_CAMCORDER_VIDEO_STREAM_ID_NORMAL) ||
+               (cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_EXTRA_PREVIEW] && stream->extra_stream_id >= MM_CAMCORDER_VIDEO_STREAM_ID_EXTRA_MIN)) {
                camera_create_preview_frame(stream, num_buffer_fd, buffer_bo_handle, &data_bo_handle, &frame);
 
                /* set stream data for camera_get_preview_frame_rotation() */
                cb_info->stream_data = stream;
 
-               if (stream->extra_stream_id < 0) {
+               if (stream->extra_stream_id < MM_CAMCORDER_VIDEO_STREAM_ID_EXTRA_MIN) {
                        monitoring_info = cb_info->monitoring_info_preview;
                        __camera_preview_cb_monitoring_info_start(monitoring_info);
 
@@ -484,20 +484,25 @@ static void __camera_event_handler_preview(camera_cb_info_s *cb_info, char *recv
                cb_info->stream_data = NULL;
        }
 
-       if (stream->extra_stream_id >= 0)
+       if (stream->extra_stream_id >= MM_CAMCORDER_VIDEO_STREAM_ID_EXTRA_MIN)
                goto _PREVIEW_CB_HANDLER_DONE;
 
        /* make media packet with below cases.
         * 1. media_packet_preview_cb is set
-        * 2. EVAS display rendering
-        * 3. media bridge is set
+        * 2. media_packet_preview_internal_cb is set
+        * 3. EVAS display rendering
+        * 4. media bridge is set
         */
+       g_mutex_lock(&cb_info->bridge_lock);
+
        if (cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW] ||
+               cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL] ||
                cb_info->is_evas_render ||
                cb_info->bridge) {
                ret = __camera_create_media_packet_data(ret_fd, tfd, num_buffer_fd, bo, buffer_bo, data_bo, &mp_data);
                if (ret != CAMERA_ERROR_NONE) {
                        CAM_LOG_ERROR("__camera_create_media_packet_data failed[0x%x]", ret);
+                       g_mutex_unlock(&cb_info->bridge_lock);
                        goto _PREVIEW_CB_HANDLER_DONE;
                }
 
@@ -506,12 +511,14 @@ static void __camera_event_handler_preview(camera_cb_info_s *cb_info, char *recv
                        CAM_LOG_ERROR("__camera_create_media_packet failed[0x%x]", ret);
                        __camera_release_media_packet_data(mp_data, cb_info);
                        mp_data = NULL;
+                       g_mutex_unlock(&cb_info->bridge_lock);
                        goto _PREVIEW_CB_HANDLER_DONE;
                }
        }
 
        /* 1. media packet preview callback */
-       if (cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW]) {
+       if (cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW] &&
+               stream->extra_stream_id == MM_CAMCORDER_VIDEO_STREAM_ID_NORMAL) {
                media_packet_ref(pkt);
 
                monitoring_info = cb_info->monitoring_info_preview_mp;
@@ -523,8 +530,20 @@ static void __camera_event_handler_preview(camera_cb_info_s *cb_info, char *recv
                __camera_preview_cb_monitoring_info_end(monitoring_info);
        }
 
-       /* 2. call evas renderer */
-       if (cb_info->is_evas_render) {
+//LCOV_EXCL_START
+       /* 2. media packet preview internal callback */
+       if (cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL] &&
+               stream->extra_stream_id == MM_CAMCORDER_VIDEO_STREAM_ID_INTERNAL) {
+               media_packet_ref(pkt);
+
+               ((camera_media_packet_preview_cb)cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL])(pkt,
+                       cb_info->user_data[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL]);
+       }
+//LCOV_EXCL_STOP
+
+       /* 3. call evas renderer */
+       if (cb_info->is_evas_render &&
+               stream->extra_stream_id == MM_CAMCORDER_VIDEO_STREAM_ID_NORMAL) {
                if (cb_info->run_evas_render) {
                        media_packet_ref(pkt);
                        ret = mm_display_interface_evas_render(cb_info->dp_interface, pkt);
@@ -538,10 +557,9 @@ static void __camera_event_handler_preview(camera_cb_info_s *cb_info, char *recv
        }
 
 //LCOV_EXCL_START
-       /* 3. media bridge */
-       g_mutex_lock(&cb_info->bridge_lock);
-
-       if (cb_info->bridge) {
+       /* 4. media bridge */
+       if (cb_info->bridge &&
+               stream->extra_stream_id == MM_CAMCORDER_VIDEO_STREAM_ID_NORMAL) {
                media_packet_ref(pkt);
                ret = media_bridge_push_packet(cb_info->bridge, pkt);
                if (ret != MEDIA_BRIDGE_ERROR_NONE) {
@@ -558,7 +576,8 @@ _PREVIEW_CB_HANDLER_DONE:
           and preview callback(normal or media packet) is set. */
        if (num_buffer_fd > 0 &&
                (cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_PREVIEW] ||
-                cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW]))
+                       cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW] ||
+                       cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL]))
                _camera_msg_send(MUSE_CAMERA_API_PREVIEW_CB_RETURN, NULL, cb_info, NULL, 0);
 
        if (pkt) {
@@ -790,7 +809,7 @@ static void __camera_release_imported_bo(tbm_bo *bo)
 }
 
 
-static bool __is_supported(camera_h camera, muse_camera_api_e api)
+bool __is_supported(camera_h camera, muse_camera_api_e api)
 {
        int ret = CAMERA_ERROR_NONE;
        camera_cli_s *pc = (camera_cli_s *)camera;
@@ -1557,6 +1576,7 @@ static void __camera_client_user_callback(camera_cb_info_s *cb_info, char *recv_
        if (cb_info->user_cb[event] == NULL) {
                if (event != MUSE_CAMERA_EVENT_TYPE_PREVIEW &&
                        event != MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW &&
+                       event != MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL &&
                        event != MUSE_CAMERA_EVENT_TYPE_FACE_DETECTION &&
                        event != MUSE_CAMERA_EVENT_TYPE_CAPTURE) {
                        g_mutex_unlock(&cb_info->user_cb_mutex[event]);
@@ -1567,6 +1587,7 @@ static void __camera_client_user_callback(camera_cb_info_s *cb_info, char *recv_
                /* return buffer message should be sent for some events.
                 - MUSE_CAMERA_EVENT_TYPE_PREVIEW
                 - MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW
+                - MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL
                 - MUSE_CAMERA_EVENT_TYPE_FACE_DETECTION
                 - MUSE_CAMERA_EVENT_TYPE_CAPTURE
                */
@@ -1607,6 +1628,7 @@ static void __camera_client_user_callback(camera_cb_info_s *cb_info, char *recv_
                break;
        case MUSE_CAMERA_EVENT_TYPE_PREVIEW:
        case MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW:
+       case MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL:
                __camera_event_handler_preview(cb_info, recv_msg, tfd);
                break;
 //LCOV_EXCL_START
index f19a1d8..291e3ff 100644 (file)
@@ -481,4 +481,79 @@ int camera_attr_get_preview_frame_status_auto_white_balance(camera_h camera, cam
 
        return CAMERA_ERROR_NONE;
 }
+
+
+bool camera_is_supported_media_packet_preview_internal_cb(camera_h camera)
+{
+       return __is_supported(camera, MUSE_CAMERA_API_SUPPORT_MEDIA_PACKET_PREVIEW_INTERNAL_CB);
+}
+
+
+int camera_set_media_packet_preview_internal_cb(camera_h camera, camera_media_packet_preview_cb callback, void *user_data)
+{
+       int ret = CAMERA_ERROR_NONE;
+       camera_cli_s *pc = (camera_cli_s *)camera;
+       muse_camera_api_e api = MUSE_CAMERA_API_SET_MEDIA_PACKET_PREVIEW_INTERNAL_CB;
+
+       CAMERA_CHECK_HANDLE_RETURN_VAL(pc, CAMERA_ERROR_INVALID_PARAMETER);
+
+       if (camera_is_supported_media_packet_preview_internal_cb(camera) == false) {
+               CAM_LOG_ERROR("NOT SUPPORTED");
+               return CAMERA_ERROR_NOT_SUPPORTED;
+       }
+
+       if (callback == NULL) {
+               CAM_LOG_ERROR("NULL callback");
+               return CAMERA_ERROR_INVALID_PARAMETER;
+       }
+
+       CAM_LOG_INFO("Enter");
+
+       _camera_msg_send(api, NULL, pc->cb_info, &ret, CAMERA_CB_TIMEOUT);
+
+       if (ret == CAMERA_ERROR_NONE) {
+               g_mutex_lock(&pc->cb_info->user_cb_mutex[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL]);
+
+               pc->cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL] = callback;
+               pc->cb_info->user_data[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL] = user_data;
+
+               g_mutex_unlock(&pc->cb_info->user_cb_mutex[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL]);
+       }
+
+       CAM_LOG_INFO("ret : 0x%x", ret);
+
+       return ret;
+}
+
+
+int camera_unset_media_packet_preview_internal_cb(camera_h camera)
+{
+       int ret = CAMERA_ERROR_NONE;
+       camera_cli_s *pc = (camera_cli_s *)camera;
+       muse_camera_api_e api = MUSE_CAMERA_API_UNSET_MEDIA_PACKET_PREVIEW_INTERNAL_CB;
+
+       CAMERA_CHECK_HANDLE_RETURN_VAL(pc, CAMERA_ERROR_INVALID_PARAMETER);
+
+       if (camera_is_supported_media_packet_preview_internal_cb(camera) == false) {
+               CAM_LOG_ERROR("NOT SUPPORTED");
+               return CAMERA_ERROR_NOT_SUPPORTED;
+       }
+
+       CAM_LOG_INFO("Enter");
+
+       _camera_msg_send(api, NULL, pc->cb_info, &ret, CAMERA_CB_TIMEOUT);
+
+       if (ret == CAMERA_ERROR_NONE) {
+               g_mutex_lock(&pc->cb_info->user_cb_mutex[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL]);
+
+               pc->cb_info->user_cb[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL] = NULL;
+               pc->cb_info->user_data[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL] = NULL;
+
+               g_mutex_unlock(&pc->cb_info->user_cb_mutex[MUSE_CAMERA_EVENT_TYPE_MEDIA_PACKET_PREVIEW_INTERNAL]);
+       }
+
+       CAM_LOG_INFO("ret : 0x%x", ret);
+
+       return ret;
+}
 //LCOV_EXCL_STOP
index 69435a8..de8eff2 100644 (file)
@@ -36,6 +36,7 @@ static int g_camera_device_connection_changed_cb_id;
 static int g_camera_preview_cb_dump;
 static int g_camera_extra_preview_cb_dump;
 static int g_camera_mp_preview_cb_dump;
+static int g_camera_mp_preview_internal_cb_dump;
 
 static struct timeval previous_time;
 static struct timeval current_time;
@@ -278,6 +279,7 @@ const char *facing_direction[] = {
 enum {
        PREVIEW_CB_TYPE_NORMAL = 0,
        PREVIEW_CB_TYPE_MEDIA_PACKET,
+       PREVIEW_CB_TYPE_MEDIA_PACKET_INTERNAL,
        PREVIEW_CB_TYPE_EXTRA
 };
 
@@ -605,7 +607,7 @@ _DUMP_MEDIA_PACKET_DATA_OUT:
                fclose(fp);
 }
 
-static void _camera_media_packet_preview_cb(media_packet_h pkt, void *user_data)
+static void _camera_media_packet_preview_cb_common(media_packet_h pkt, void *user_data, int do_dump, const char *dump_path)
 {
        int ret = 0;
        int width = 0;
@@ -632,8 +634,8 @@ static void _camera_media_packet_preview_cb(media_packet_h pkt, void *user_data)
 
        g_print("[MP_PREVIEW_CB] media_packet_preview_cb[mimetype:0x%x, %dx%d]\n", type, width, height);
 
-       if (g_camera_mp_preview_cb_dump)
-               _dump_media_packet_data(pkt, MP_PREVIEW_CB_DUMP_FILE_NAME);
+       if (do_dump)
+               _dump_media_packet_data(pkt, dump_path);
 
 _MEDIA_PACKET_PREVIEW_CB_OUT:
        if (fmt) {
@@ -644,6 +646,24 @@ _MEDIA_PACKET_PREVIEW_CB_OUT:
        media_packet_unref(pkt);
 }
 
+static void _camera_media_packet_preview_cb(media_packet_h pkt, void *user_data)
+{
+       _camera_media_packet_preview_cb_common(pkt,
+               user_data,
+               g_camera_mp_preview_cb_dump,
+               MP_PREVIEW_CB_DUMP_FILE_NAME);
+}
+
+
+static void _camera_media_packet_preview_internal_cb(media_packet_h pkt, void *user_data)
+{
+       g_print("[INTERNAL]");
+       _camera_media_packet_preview_cb_common(pkt,
+               user_data,
+               g_camera_mp_preview_internal_cb_dump,
+               MP_PREVIEW_INTERNAL_CB_DUMP_FILE_NAME);
+}
+
 
 static int __set_preview_cb_common(int *preview_cb_dump, set_preview_cb set_cb, preview_cb callback)
 {
@@ -662,7 +682,7 @@ static int _camera_set_preview_cb(int type)
 {
        int set_cb = 0;
 
-       g_print("* Preview Callback[type:%d,0:NORMAL,1:MEDIAPACKET,2:EXTRA]\n", type);
+       g_print("* Preview Callback[type:%d,0:NORMAL,1:MEDIAPACKET,2:MEDIAPACKET_INTERNAL,3:EXTRA]\n", type);
 
        g_print("\tUnset[0] / Set[Others] :");
 
@@ -686,6 +706,13 @@ static int _camera_set_preview_cb(int type)
                        return __set_preview_cb_common(&g_camera_mp_preview_cb_dump,
                                (set_preview_cb)camera_set_media_packet_preview_cb,
                                (preview_cb)_camera_media_packet_preview_cb);
+       case PREVIEW_CB_TYPE_MEDIA_PACKET_INTERNAL:
+               if (set_cb == 0)
+                       return camera_unset_media_packet_preview_internal_cb(hcamcorder->camera);
+               else
+                       return __set_preview_cb_common(&g_camera_mp_preview_internal_cb_dump,
+                               (set_preview_cb)camera_set_media_packet_preview_internal_cb,
+                               (preview_cb)_camera_media_packet_preview_internal_cb);
        case PREVIEW_CB_TYPE_EXTRA:
                if (set_cb == 0)
                        return camera_unset_extra_preview_cb(hcamcorder->camera);
@@ -876,6 +903,7 @@ void print_menu()
                g_print("\t   '5' Set/Unset preview callback\n");
                g_print("\t   '6' Set/Unset extra preview callback\n");
                g_print("\t   '7' Set/Unset media packet preview callback\n");
+               g_print("\t   '8' Set/Unset media packet preview internal callback\n");
                g_print("\t   'b' back\n");
                g_print("\t=======================================\n");
                break;
@@ -1026,6 +1054,10 @@ static void main_menu(gchar buf)
                err = _camera_set_preview_cb(PREVIEW_CB_TYPE_MEDIA_PACKET);
                g_print("\tresult[0x%x]\n\n", err);
                break;
+       case '8':
+               err = _camera_set_preview_cb(PREVIEW_CB_TYPE_MEDIA_PACKET_INTERNAL);
+               g_print("\tresult[0x%x]\n\n", err);
+               break;
        case 'b': /* back */
                __release_media_bridge();
 
index 4e5941d..4b90805 100644 (file)
 /*-----------------------------------------------------------------------
 |    LOCAL #defines:                                                    |
 -----------------------------------------------------------------------*/
-#define DEFAULT_FILE_PATH               "/home/owner/media"
-#define PREVIEW_CB_DUMP_FILE_NAME       "preview.data"
-#define EXTRA_PREVIEW_CB_DUMP_FILE_NAME "extra_preview.data"
-#define MP_PREVIEW_CB_DUMP_FILE_NAME    "mp_preview.data"
-#define MAX_FILE_NAME_LENGTH            256
-#define MAX_FILE_PATH_LENGTH            (MAX_FILE_NAME_LENGTH - 20)
+#define DEFAULT_FILE_PATH                       "/home/owner/media"
+#define PREVIEW_CB_DUMP_FILE_NAME               "preview.data"
+#define EXTRA_PREVIEW_CB_DUMP_FILE_NAME         "extra_preview.data"
+#define MP_PREVIEW_CB_DUMP_FILE_NAME            "mp_preview.data"
+#define MP_PREVIEW_INTERNAL_CB_DUMP_FILE_NAME   "mp_preview_internal.data"
+#define MAX_FILE_NAME_LENGTH                    256
+#define MAX_FILE_PATH_LENGTH                    (MAX_FILE_NAME_LENGTH - 20)
 
 #define CHECK_MM_ERROR(expr) \
        do {\