jrtc video call
authorCCC <2393524978@qq.com>
Tue, 27 Jun 2023 02:49:39 +0000 (10:49 +0800)
committerCCC <2393524978@qq.com>
Tue, 27 Jun 2023 02:49:39 +0000 (10:49 +0800)
19 files changed:
2.sdk/external/third-party-libs/juphoon/libjrtc.a
2.sdk/gui/lv_watch/CMakeLists.txt
2.sdk/gui/lv_watch/lv_apps/camera/camera.c
2.sdk/gui/lv_watch/lv_apps/mmi_video_call/mmi_jrtc_client.c [new file with mode: 0644]
2.sdk/gui/lv_watch/lv_apps/mmi_video_call/mmi_video_call.c [new file with mode: 0644]
2.sdk/gui/lv_watch/lv_apps/mmi_video_call/mmi_video_call.h [new file with mode: 0644]
2.sdk/gui/lv_watch/lv_apps/mmi_wechat/wechat.h [moved from 2.sdk/gui/lv_watch/lv_apps/wechat/wechat.h with 100% similarity]
2.sdk/gui/lv_watch/lv_apps/mmi_wechat/wechat_ui.c [moved from 2.sdk/gui/lv_watch/lv_apps/wechat/wechat_ui.c with 100% similarity]
2.sdk/gui/lv_watch/lv_apps/primary_menu/primary_menu.c
2.sdk/gui/lv_watch/lv_apps/primary_menu/primary_menu_data.c
2.sdk/gui/lv_watch/lv_watch.h
2.sdk/gui/lv_watch/lv_watch_res.h
2.sdk/gui/lv_watch/resource/png/video_call/ICON_VIDEO_CALL_VIDEO_BTN.png [new file with mode: 0644]
2.sdk/gui/lv_watch/resource/png/video_call/ICON_VIDEO_CALL_VOICE_BTN.png [new file with mode: 0644]
2.sdk/product/craneg_modem/config/watch/lv_watch_conf.h
2.sdk/product/craneg_modem/framework/inc/gui/message/mmi_msg_struct.h
2.sdk/product/craneg_modem/framework/inc/jrtc.h
2.sdk/product/craneg_modem/framework/inc/jrtc0.h [new file with mode: 0644]
2.sdk/product/craneg_modem/lv_gui_main.c

index 6cb0419..35a55f1 100644 (file)
Binary files a/2.sdk/external/third-party-libs/juphoon/libjrtc.a and b/2.sdk/external/third-party-libs/juphoon/libjrtc.a differ
index e4ab7a9..f3fbec1 100644 (file)
@@ -147,8 +147,8 @@ set(lv_watch_sources
   lv_apps/alarm/alarm_set.c
   lv_apps/upgrade/upgrade.c
   lv_apps/quick_function/quick_function.c
-  lv_apps/video_call/video_call.c
-  lv_apps/video_call/jrtc_client.c
+  #lv_apps/video_call/video_call.c
+  #lv_apps/video_call/jrtc_client.c
   lv_apps/engineer_mode/engineer_mode_main.c
   lv_apps/engineer_mode/engineer_mode_telephony.c
   lv_apps/engineer_mode/engineer_mode_connectivity.c
@@ -202,8 +202,11 @@ set(lv_watch_sources
        mmi_location/mmi_location.c
        mmi_sundry/mmi_sundry.c
        mmi_sundry/mmi_common.c
+#video call
+       lv_apps/mmi_video_call/mmi_video_call.c
+       lv_apps/mmi_video_call/mmi_jrtc_client.c
 #wechat
-       lv_apps/wechat/wechat_ui.c
+       lv_apps/mmi_wechat/wechat_ui.c
 #primary menu data
        lv_apps/primary_menu/primary_menu_data.c
 #health_measure
index f428a50..58440de 100644 (file)
@@ -110,8 +110,8 @@ lv_obj_t * camera_create(lv_obj_t * activity_obj)
 
     cam_start_preview_t cam_params;
 
-    cam_params.width = 240;
-    cam_params.height = 240;
+    cam_params.width = LV_HOR_RES; // 240;
+    cam_params.height = LV_VER_RES; // 240;
     cam_params.sensor_id = 0;
     Hal_Camera_Start_Preview(&cam_params);
 
diff --git a/2.sdk/gui/lv_watch/lv_apps/mmi_video_call/mmi_jrtc_client.c b/2.sdk/gui/lv_watch/lv_apps/mmi_video_call/mmi_jrtc_client.c
new file mode 100644 (file)
index 0000000..fbdc5d3
--- /dev/null
@@ -0,0 +1,264 @@
+
+
+
+#include "lv_watch.h"
+
+#ifdef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+
+//#include <jrtc.h>
+#include "../../../../product/craneg_modem/framework/inc/jrtc.h"
+
+
+typedef enum jrtc_state video_call_jrtc_state_e;
+typedef enum jrtc_error video_call_jrtc_error_e;
+typedef struct jrtc_image_t  video_call_jrtc_image_t;
+
+typedef struct 
+{
+    void /*lv_task_t*/ *state_task;
+    video_call_jrtc_image_t txframe;
+    video_call_jrtc_image_t rxframe;
+    void* jrtc_ptr;
+    TIMER_ID closed_timer_id;
+    uint8_t closing;
+} video_call_jrtc_t;
+
+
+extern voip_phone_call_params_t voipcall_params;
+static video_call_jrtc_t voip_jrtc;
+
+
+static uint8_t voip_jrtc_get_image(void * preview_buf);
+
+extern UINT32  OsaGetTicks(void *pForFutureUse);
+
+
+void mmi_video_call_jrtc_config_init(void)
+{
+       char appkey[24+1] = "dc1db81c19d1da4f5fb4a197";
+       char key[16+1] = "K75A1vS562671d38";
+       char uid[64+1] = {0};
+       char tokens[128+1] = "";
+       
+       char imei[16] = {0};
+       app_adp_get_imei_req(imei);
+       strncpy(uid, imei, 16);
+
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+       
+       jrtc_config(appkey, uid, key, tokens);
+}
+
+static void mmi_video_call_jrtc_clean_up(void)
+{
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+
+    if(NULL != voip_jrtc.state_task) 
+       {
+               VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+               
+        lv_task_del(voip_jrtc.state_task);
+    }
+
+    Hal_Mem_Set(&voip_jrtc, 0, sizeof(video_call_jrtc_t));
+}
+
+static void mmi_video_call_jrtc_timeout_cb(void * para)
+{
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+
+    mmi_video_call_jrtc_clean_up();
+}
+
+static void mmi_video_call_jrtc_on_state(lv_task_t * task)
+{
+    static int8_t oldstate = -1;
+    video_call_jrtc_state_e state = jrtc_state(voip_jrtc.jrtc_ptr);
+
+    if(state != oldstate) 
+       {
+               VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: oldstate=%d,state=%d\n", __FUNCTION__, __LINE__, oldstate, state);
+    }
+
+    switch(state) 
+       {
+           case JRTC_CLOSED:
+               if((NULL != voip_jrtc.jrtc_ptr) && (NULL == voip_jrtc.closed_timer_id)) 
+                       {
+                       video_call_jrtc_error_e err = jrtc_error(voip_jrtc.jrtc_ptr);
+                               
+                               VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: error=%d\n", __FUNCTION__, __LINE__, err);
+
+                   if(!voip_jrtc.closing) 
+                               {
+                       jrtc_close(voip_jrtc.jrtc_ptr, JRTC_ENIL);
+                       voip_jrtc.closing = 1;
+                   }
+
+                   voip_jrtc.closed_timer_id = Hal_Timer_Start(100, mmi_video_call_jrtc_timeout_cb, NULL, 0);
+               }
+
+               if(MMI_VOIP_CALL_STATE_NULL != voipcall_params.state) 
+                       {
+                   watch_voip_call_response_no_carrier(0, 1);
+               }
+               break;
+
+           case JRTC_LEAVING:
+               break;
+
+           case JRTC_OPENED:
+               break;
+
+           case JRTC_JOINING:
+               break;
+
+           case JRTC_JOINED:
+               break;
+
+           case JRTC_TALKING:
+               if(MMI_VOIP_CALL_STATE_ACTIVE != voipcall_params.state) 
+                       {
+                   watch_voip_call_response_connect(0);
+               }
+
+               /* Get image */
+               if (MODEM_VIDEO_CALL_TYPE_VIDEO == voipcall_params.voip_call_type) 
+                       {
+                   void *buf = voip_phone_get_video_buffer();
+                   if(voip_jrtc_get_image(buf)) 
+                               {
+                       lv_canvas_set_buffer(voip_phone_get_ext()->canvas, buf, canvas_width, canvas_heigh, LV_IMG_CF_TRUE_COLOR);
+                   }
+               }
+               break;
+
+           default:
+               break;
+    }
+
+    oldstate = state;
+}
+
+
+static int8_t mmi_video_call_jrtc_open(const char* roomid, MODEM_VIDEO_CALL_TYPE calltype)
+{
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: %s,%d\n", __FUNCTION__, __LINE__, roomid, calltype);
+
+    Hal_Mem_Set(&voip_jrtc, 0, sizeof(video_call_jrtc_t));
+       
+    if (MODEM_VIDEO_CALL_TYPE_VIDEO == calltype) 
+       {
+        voip_jrtc.rxframe.width  = voip_jrtc.txframe.width  = 240; // 176;
+        voip_jrtc.rxframe.height = voip_jrtc.txframe.height = 320; // 144;
+        voip_jrtc.rxframe.fps    = voip_jrtc.txframe.fps    = 8; // 10;
+        voip_jrtc.rxframe.kbps   = voip_jrtc.txframe.kbps   = 120; // 240;
+
+        voip_jrtc.jrtc_ptr= jrtc_open(roomid, 0, &voip_jrtc.rxframe, &voip_jrtc.txframe);
+    } 
+       else 
+       {
+        voip_jrtc.jrtc_ptr= jrtc_open(roomid, 0, NULL, NULL);
+    }
+
+    if (!voip_jrtc.jrtc_ptr) 
+       {
+               VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+        mmi_video_call_jrtc_clean_up();
+        return -1;
+    }
+    jrtc_activate(voip_jrtc.jrtc_ptr, JRTC_AUDIO|JRTC_CAMERA);
+
+    voip_jrtc.state_task = lv_task_create(mmi_video_call_jrtc_on_state, VIDEO_RENDER_PERIOD, LV_TASK_PRIO_HIGH, NULL);
+    return 0;
+}
+
+int8_t mmi_video_call_dial_req(MODEM_VIDEO_CALL_TYPE calltype, char * pContactUri)
+{
+    int8_t ret = -1;
+
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: %s,%d\n", __FUNCTION__, __LINE__, pContactUri, calltype);
+    if (NULL == voip_jrtc.jrtc_ptr)
+        ret = mmi_video_call_jrtc_open(pContactUri, calltype);
+
+    return ret;
+}
+
+void mmi_video_call_hangup_req(BOOL bye)
+{
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: %p,%d\n", __FUNCTION__, __LINE__, voip_jrtc.jrtc_ptr, bye);
+
+    if (NULL != voip_jrtc.jrtc_ptr) 
+       {
+        video_call_jrtc_error_e err = (TRUE == bye) ? JRTC_EBYE : JRTC_EAUDIOCALL;
+
+        jrtc_close(voip_jrtc.jrtc_ptr, err);
+        voip_jrtc.closing = 1;
+    } 
+       else
+       {
+        watch_voip_call_response_no_carrier(0, 0);
+    }
+}
+
+int8_t mmi_video_call_answer_req(void)
+{
+    int8_t ret = -1;
+
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: %p\n", __FUNCTION__, __LINE__, voip_jrtc.jrtc_ptr);
+    if (NULL == voip_jrtc.jrtc_ptr)
+        ret = mmi_video_call_jrtc_open(voipcall_params.number, voipcall_params.voip_call_type);
+
+    return ret;
+}
+
+void mmi_video_call_reject_req(BOOL bye)
+{
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: %p,%d\n", __FUNCTION__, __LINE__, voip_jrtc.jrtc_ptr, bye);
+
+    if (NULL != voip_jrtc.jrtc_ptr) 
+       {
+        video_call_jrtc_error_e err = (TRUE == bye) ? JRTC_EBYE : JRTC_EAUDIOCALL;
+
+        jrtc_close(voip_jrtc.jrtc_ptr, err);
+        voip_jrtc.closing = 1;
+    } 
+       else 
+       {
+        watch_voip_call_response_no_carrier(0, 0);
+    }
+}
+
+
+//extern unsigned yuv420_scale(int src_width, int src_height, int dst_width, int dst_height,     unsigned char *src_buf, unsigned char *dst_buf, unsigned char semi);
+//extern unsigned rgb565_scale(int32_t src_width, int32_t src_height, int32_t dst_width, int32_t dst_height, uint8_t *src_buf, uint8_t *dst_buf);
+//extern void yuv420_2_rgb565(int width, int height, const unsigned char *src, unsigned short *dst, unsigned semi);
+
+static uint8_t voip_jrtc_get_image(void * preview_buf)
+{
+    uint8_t ret = 0;
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+    LV_ASSERT_MEM(ext);
+
+    video_call_jrtc_image_t * frame = NULL;
+    if(ext->local_remote_switch_flag == VOIP_REMOTE_SWITCH_REMOTE)
+        frame = &voip_jrtc.rxframe;
+    else
+        frame = &voip_jrtc.txframe;
+
+    if (frame->put != frame->get) {
+#if (ENABLE_VIDEO_SCALE == 0)
+        yuv420_2_rgb565(frame->width, frame->height, frame->data, preview_buf, frame->format);
+#else
+        Hal_Yuv420_Render(frame->width, frame->height, canvas_width, canvas_heigh, frame->data, preview_buf, frame->format);
+#endif
+
+        frame->get = frame->put;
+        ret = 1;
+        voip_fps_check(frame->fps);
+    }
+
+    return ret;
+}
+
+#endif // #if USE_LV_WATCH_VIDEOCALL != 0
diff --git a/2.sdk/gui/lv_watch/lv_apps/mmi_video_call/mmi_video_call.c b/2.sdk/gui/lv_watch/lv_apps/mmi_video_call/mmi_video_call.c
new file mode 100644 (file)
index 0000000..eedf3f0
--- /dev/null
@@ -0,0 +1,2044 @@
+/**
+ * @file videocall.c
+ *
+ */
+
+/*********************
+*      INCLUDES
+*********************/
+#include "lv_watch.h"
+
+
+#ifdef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+
+#include "mmi_video_call.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+
+static lv_signal_cb_t ancestor_signal;
+#if VIDEO_SIGNAL_CHECK
+static lv_task_t * signal_check_task = NULL;
+#endif
+static lv_task_t * rat_check_task = NULL;
+
+/**********************
+*      Global Variable
+**********************/
+voip_phone_call_params_t voipcall_params;
+lv_color_t g_cbuf[canvas_width * canvas_heigh + 8]  __attribute__((aligned(8)));
+
+/**********************
+*  STATIC PROTOTYPES
+**********************/
+static lv_obj_t * voip_phone_cont_create(lv_obj_t * par, lv_align_t cont_layout, lv_style_t * style, lv_coord_t width);
+static lv_obj_t * voip_ring_off_content_create(lv_obj_t * par);
+static void voip_phone_dialing(lv_obj_t * content);
+static void voip_phone_ring(lv_obj_t * content);
+static void voip_phone_connected(lv_obj_t * content);
+static void voip_phone_left_slide_callback(lv_obj_t * obj);
+static void voip_phone_right_slide_callback(lv_obj_t * obj);
+static void voip_phone_down_slide_callback(lv_obj_t * obj);
+static void voip_dialing_animation(lv_obj_t * par);
+static void voip_ring_animation(lv_obj_t * par);
+static void voip_connected_animation(lv_obj_t * obj);
+static void voip_volume_btn_action(lv_obj_t * obj, lv_event_t e);
+static void voip_slider_action(lv_obj_t * slider, lv_event_t e);
+static void voip_ring_hook_action(lv_obj_t * btn, lv_event_t e);
+static void voip_phone_ring_off_timeout_cb(void * para);
+static void voip_phone_callreq_timeout_cb(void * para);
+static void voip_phone_cleanup(void);
+static void voip_phone_obj_hide_anim_end(lv_anim_t * anim);
+static void voip_anim_fp_dialing_ring(lv_obj_t * obj, int32_t x);
+static void voip_anim_fp_connected(lv_anim_t * anim);
+static void voip_phone_prepare_destory(lv_obj_t * activity_obj);
+static lv_obj_t * voip_phone_get_activity_obj(void);
+static lv_obj_t * voip_phone_get_obj(void);
+static void voip_get_time_elaps(uint32_t last_time, char * str, int8_t size);
+static lv_obj_t * voip_phone_create(lv_obj_t * activity_obj, voip_phone_ui_e phone_ui_id);
+static lv_res_t voip_phone_ring_off_create(void);
+static void voip_ring_off_action(lv_obj_t * btn, lv_event_t e);
+static void voip_set_volume_when_slider_act(void);
+static void voip_local_remote_switch_action(lv_obj_t * btn, lv_event_t e);
+static void canvas_button_hidden_event_cb(lv_obj_t * obj, lv_event_t e);
+static void video_call_add_element(lv_obj_t * element);
+static void voip_signal_check_start(void * ext);
+static void voip_signal_check_stop(void);
+static bool is_rat_gsm(void);
+static void voip_rat_check_start(void);
+static void voip_rat_check_stop(void);
+
+#if VOIP_PHONE_TEST
+static void voip_phone_connected_cb(void * para);
+static void watch_voip_call_response_originating(int32_t index);
+#endif
+
+
+void voip_audio_ctrl_callback(AUDIO_CTRL_PRIORITY priority)
+{
+    printf("%s,priority is %d\n", __FUNCTION__,priority);
+    if(AUDIO_CTRL_PRIORITY_2 > priority)
+    {
+        lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_VOIPPHONE);
+        if(activity_obj == NULL) return;
+        voip_activity_destroy_req();
+        Hal_Audio_Manage_End_Ind(AUDIO_CTRL_PRIORITY_2);
+    }
+}
+
+static lv_voip_phonebook_obj_ext_t * voip_phonebook_get_ext(void)
+{
+    // printf("%s %d\n", __FUNCTION__, __LINE__);
+    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_VOIP_PHONEBOOK);
+
+    if(activity_obj == NULL) {
+        return(NULL);
+    }
+
+    lv_obj_t * watch_obj;
+    lv_watch_get_child_obj(activity_obj, lv_watch_obj_signal, &watch_obj);
+    if(watch_obj == NULL) {
+        return(NULL);
+    }
+
+    if(watch_obj) {
+        lv_voip_phonebook_obj_ext_t * ext = lv_obj_get_ext_attr(watch_obj);
+        return(ext);
+    }
+    return(NULL);
+}
+
+static int voip_aligned_width(int val, lv_coord_t move_width)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    bool negative = (val < 0) ? true : false;
+    if(val < 0) {
+        val = -val;
+    }
+    int i       = (val + move_width - 1) / move_width - 1;
+    int new_val = i * move_width;
+
+    new_val = (val - new_val > move_width / 2) ? (new_val + move_width) : new_val;
+    new_val = negative ? -new_val : new_val;
+    return(new_val);
+}
+
+static lv_res_t voip_page_scrl_signal_handler(lv_obj_t * page_scrl, lv_signal_t sign, void * param)
+{
+    // printf("%s %d\n", __FUNCTION__, __LINE__);
+    lv_voip_phonebook_obj_ext_t * ext = voip_phonebook_get_ext();
+
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return(LV_RES_INV);
+    }
+
+    if((sign == LV_SIGNAL_DRAG_END) || (sign == LV_SIGNAL_RELEASED)) {
+        lv_coord_t new_x         = lv_obj_get_x(page_scrl);
+        lv_coord_t aligned_new_x = voip_aligned_width(new_x, ext->move_width);
+        lv_obj_set_x(page_scrl, aligned_new_x);
+
+        lv_bidi_dir_t dir = lv_obj_get_base_dir(page_scrl);
+
+        /* update image id */
+        ext->cur_img_id = (aligned_new_x < 0) ? -aligned_new_x / ext->move_width : aligned_new_x / ext->move_width;
+        voip_phonebook_t * contact;
+        if(LV_BIDI_DIR_RTL == dir) {
+            contact = _lv_ll_get_tail(&ext->contact_list);
+        } else {
+            contact = _lv_ll_get_head(&ext->contact_list);
+        }
+        uint8_t i = 0;
+        while(NULL != contact) {
+            if(ext->cur_img_id == i) {
+                ext->cur_contact = contact;
+                break;
+            }
+            i++;
+            if(LV_BIDI_DIR_RTL == dir) {
+                contact = _lv_ll_get_prev(&ext->contact_list, contact);
+            } else {
+                contact = _lv_ll_get_next(&ext->contact_list, contact);
+            }
+        }
+        if(NULL == contact) {
+            printf("error, contact not found in voip_page_scrl_signal_handler\n");
+            return(LV_RES_INV);
+        }
+        lv_label_set_text(ext->label_phone_contact, contact->name);
+        if(0 < strlen(contact->number)) {
+            uint32_t pos = LV_LABEL_POS_LAST;
+            if(LV_BIDI_DIR_RTL == dir) {
+                pos = 0;
+            }
+            if(0 < strlen(contact->name)) {
+                lv_label_ins_text(ext->label_phone_contact, pos, "  ");
+            }
+            lv_label_ins_text(ext->label_phone_contact, pos, contact->number);
+        }
+    }
+
+    return(LV_RES_OK);
+} /* voip_page_scrl_signal_handler */
+
+static lv_res_t voip_phonebook_page_scrl_signal(lv_obj_t * page_scrl, lv_signal_t sign, void * param)
+{
+    // printf("%s %d\n", __FUNCTION__, __LINE__);
+    lv_res_t res;
+
+    /* Include the ancient signal function */
+    res = ancestor_signal(page_scrl, sign, param);
+    if(res != LV_RES_OK) {
+        return(res);
+    }
+
+    if(sign == LV_SIGNAL_CLEANUP) {
+        /*when cleanup, can not invoke lv_indev_get_act */
+        return(LV_RES_OK);
+    }
+
+    res = voip_page_scrl_signal_handler(page_scrl, sign, param);
+    if(res != LV_RES_OK) {
+        return(res);
+    }
+
+    return(LV_RES_OK);
+}
+
+static lv_res_t voip_phonebook_element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param)
+{
+    // printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    lv_res_t res;
+
+    /* if not click signs, invoke ancient handler and return */
+    if(sign != LV_SIGNAL_PRESSING && sign != LV_SIGNAL_PRESSED && sign != LV_SIGNAL_RELEASED && sign != LV_SIGNAL_PRESS_LOST) {
+        /* Include the ancient signal function */
+        lv_signal_cb_t sign_func = (lv_signal_cb_t)(lv_obj_get_user_data(element).user_data);
+        res = sign_func(element, sign, param);
+        if(res != LV_RES_OK) {
+            return(res);
+        }
+        return(LV_RES_OK);
+    }
+
+    lv_obj_t * obj = lv_obj_get_parent(element);
+    while(obj) {
+        if(lv_obj_get_signal_cb(obj) != lv_watch_obj_signal) {
+            obj = lv_obj_get_parent(obj);
+        } else {
+            break;
+        }
+    }
+    if(obj == NULL) {
+        return(LV_RES_INV);
+    }
+
+    res = lv_watch_obj_signal(obj, sign, param);
+    if(res != LV_RES_OK) {
+        return(res);
+    }
+
+    lv_voip_phonebook_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
+    if(ext == NULL) {
+        return(LV_RES_INV);
+    }
+    if(ext->page_scrl == NULL) {
+        return(LV_RES_INV);
+    }
+    res = voip_page_scrl_signal_handler(ext->page_scrl, sign, param);
+    if(res != LV_RES_OK) {
+        return(res);
+    }
+
+    /* Include the ancient signal function */
+    lv_signal_cb_t sign_func = (lv_signal_cb_t)(lv_obj_get_user_data(element).user_data);
+    res = sign_func(element, sign, param);
+    if(res != LV_RES_OK) {
+        return(res);
+    }
+
+    return(LV_RES_OK);
+} /* voip_phonebook_element_signal_func */
+
+static void voip_phonebook_add_element(lv_obj_t * element)
+{
+    // printf("%s %d\n", __FUNCTION__, __LINE__);
+    lv_watch_set_free_ptr(element, lv_obj_get_signal_cb(element));
+    lv_obj_set_signal_cb(element, voip_phonebook_element_signal_func);
+    lv_obj_add_protect(element, LV_PROTECT_PRESS_LOST | LV_PROTECT_CLICK_FOCUS);
+}
+
+static lv_obj_t * voipcall_number_add(lv_obj_t * page, const void * img_src)
+{
+    // printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    lv_obj_t * img = NULL;
+
+    lv_obj_t * content = lv_cont_create(page, NULL);
+
+    lv_obj_add_style(content, LV_OBJ_PART_MAIN, &lv_watch_style_transp);
+    lv_obj_set_size(content,  120, lv_obj_get_height(page));
+    lv_cont_set_layout(content, LV_LAYOUT_OFF);
+    LV_ASSERT_MEM(content);
+    if(content == NULL) {
+        return(NULL);
+    }
+    /*must set click enable for these content, If not, no pressed/pressing signals out*/
+    lv_obj_set_click(content, true);
+    voip_phonebook_add_element(content);
+    /*hor drag in page, must set glue*/
+    lv_page_glue_obj(content, true);
+
+    img = lv_img_create(content, NULL);
+    lv_img_set_src(img, img_src);
+    lv_obj_align(img, content, LV_ALIGN_IN_TOP_MID, 0, 20);
+
+    return(content);
+}
+
+static lv_obj_t * voip_append_obj(lv_obj_t * page)
+{
+    // printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    lv_voip_phonebook_obj_ext_t * ext = voip_phonebook_get_ext();
+    lv_obj_t * voip_append_obj = lv_obj_create(page, NULL);
+
+    lv_obj_set_size(voip_append_obj, ext->append_width, lv_obj_get_height(page));
+    lv_obj_add_style(voip_append_obj, LV_OBJ_PART_MAIN, &lv_style_transp_tight);
+    voip_phonebook_add_element(voip_append_obj);
+    return(voip_append_obj);
+}
+
+static lv_obj_t * voip_phone_create(lv_obj_t * activity_obj, voip_phone_ui_e phone_ui_id)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    if(VOIP_PHONE_UI_CONNECTED == phone_ui_id) {
+        /* set speaker volume to nv when leaving activiry's slider */
+        voip_set_volume_when_slider_act();
+    }
+
+    /* cleanup phone first */
+    voip_phone_cleanup();
+
+    if(activity_obj == NULL) 
+       {
+        lv_watch_activity_ext_t activity_ext = {0};
+
+        activity_ext.actId           = ACT_ID_VOIPPHONE;
+        activity_ext.create          = NULL;
+        activity_ext.prepare_destory = voip_phone_prepare_destory;
+        activity_obj                 = lv_watch_creat_activity_obj(&activity_ext);
+        LV_ASSERT_MEM(activity_obj);
+        if(activity_obj == NULL) {
+            return(NULL);
+        }
+    }
+
+    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
+    LV_ASSERT_MEM(obj);
+    if(obj == NULL) {
+        return(NULL);
+    }
+    lv_watch_obj_set_anim_mode(obj, LV_WATCH_LEFT_OR_RIGHT_SLIDE_CALLBACK);
+    lv_watch_obj_set_left_slide_cb(obj, voip_phone_left_slide_callback);
+    lv_watch_obj_set_right_slide_cb(obj, voip_phone_right_slide_callback);
+
+    lv_voip_phone_obj_ext_t * ext = lv_obj_allocate_ext_attr(obj, sizeof(lv_voip_phone_obj_ext_t));
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return(NULL);
+    }
+
+    ext->phone_status_label       = NULL;
+    ext->phone_animation_obj      = NULL;
+    ext->phone_ui_t               = phone_ui_id;
+    ext->local_remote_switch_flag = VOIP_REMOTE_SWITCH_NULL;
+#if ENABLE_VIDEO_SCALE == 0
+    ext->voiph263fmt = VOIP_H263_FMT_QCIF;
+#else
+    ext->voiph263fmt = VOIP_H263_FMT_OTHER;
+#endif
+
+    hal_rtc_t rtc_curr;            // current rtc time
+    Hal_Rtc_Gettime(&rtc_curr);
+    ext->connect_start_time = time_to_seconds(&rtc_curr);
+    ext->connect_time = NULL;
+    ext->ring_value_button = NULL;
+    ext->view_switch_button = NULL;
+    ext->ring_off_button = NULL;
+    ext->canvas_button_hidden_flag = NOT_HIDDEN;
+
+    /* content */
+    lv_obj_t * content = lv_cont_create(obj, NULL);
+    lv_obj_set_size(content, lv_obj_get_width(obj), lv_obj_get_height(obj) - 20);
+    lv_cont_set_layout(content, LV_LAYOUT_COLUMN_MID);
+    LV_ASSERT_MEM(content);
+    if(content == NULL) {
+        return(NULL);
+    }
+    lv_obj_add_style(content, LV_OBJ_PART_MAIN, &lv_watch_style_transp);
+    /* must click false */
+    lv_obj_set_click(content, false);
+    lv_obj_align(content, obj, LV_ALIGN_CENTER, 0, 0);
+
+    if(ext->phone_ui_t == VOIP_PHONE_UI_DIALING) {
+        voip_phone_dialing(content);
+        voip_dialing_animation(ext->phone_status_label);
+    } else if(ext->phone_ui_t == VOIP_PHONE_UI_INCOMING) {
+        voip_phone_ring(content);
+        voip_ring_animation(ext->phone_status_label);
+    } else if(ext->phone_ui_t == PHONE_UI_CONNECTED) {
+        voip_phone_connected(content);
+        voip_connected_animation(ext->phone_status_label);
+    }
+
+    return(obj);
+} /* voip_phone_create */
+
+lv_res_t voip_phone_ring_off_create()
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return(LV_RES_INV);
+    }
+
+    // stop callreq timer
+    if(NULL != voipcall_params.callreq_timer_id) {
+        Hal_Timer_Stop(voipcall_params.callreq_timer_id);
+        voipcall_params.callreq_timer_id = NULL;
+    }
+
+    if(ext->phone_ui_t == VOIP_PHONE_UI_INCOMING || ext->phone_ui_t == VOIP_PHONE_UI_DIALING) {
+        if(ext->phone_status_label) {
+            lv_label_set_text(ext->phone_status_label, "");
+        }
+
+        voip_ring_off_content_create(voip_phone_get_obj());
+    } else if(ext->phone_ui_t == PHONE_UI_CONNECTED) {
+        voip_ring_off_content_create(voip_phone_get_obj());
+    }
+
+    if(ext->phone_animation_obj) {
+        lv_obj_del(ext->phone_animation_obj);
+        ext->phone_animation_obj = NULL;
+    }
+
+    /* set speaker volume to nv when leaving activiry's slider */
+    voip_set_volume_when_slider_act();
+
+    /* phone cleanup when anim end*/
+    lv_anim_t a;
+    lv_anim_init(&a);
+    lv_anim_set_var(&a, voip_phone_get_obj());
+    lv_anim_set_ready_cb(&a, (lv_anim_ready_cb_t)voip_phone_obj_hide_anim_end);
+    lv_anim_set_time(&a, VOIP_PHONE_RING_OFF_CONTINUE_TIME);
+    lv_anim_start(&a);
+
+    voip_signal_check_stop();
+    voip_rat_check_stop();
+
+    return(LV_RES_OK);
+} /* voip_phone_ring_off_create */
+
+void voip_ring_off_action(lv_obj_t * btn, lv_event_t e)
+{
+    if(LV_EVENT_CLICKED != e) {
+        return;
+    }
+
+    printf("%s:  %d, state=%d\n", __FUNCTION__, __LINE__, voipcall_params.state);
+
+    if(false == voipcall_params.ring_off) {
+        voipcall_params.ring_off = true;
+
+        if(NULL == voipcall_params.ring_off_timer_id) {
+            voipcall_params.ring_off_timer_id = Hal_Timer_Start(VOIP_RING_OFF_TIMER_LEN, voip_phone_ring_off_timeout_cb, NULL, 0);
+        }
+        // stop callreq timer
+        if(NULL != voipcall_params.callreq_timer_id) {
+            Hal_Timer_Stop(voipcall_params.callreq_timer_id);
+            voipcall_params.callreq_timer_id = NULL;
+        }
+    }
+
+    switch(voipcall_params.state) {
+        case MMI_VOIP_CALL_STATE_NULL:
+        case MMI_VOIP_CALL_STATE_INVALID:
+            // do noting
+            break;
+
+        case MMI_VOIP_CALL_STATE_ACTIVE:
+            mmi_video_call_hangup_req(TRUE);
+            break;
+
+        case MMI_VOIP_CALL_STATE_INCOMING:
+            Hal_Tone_Play_End();
+            mmi_video_call_reject_req(TRUE);
+            break;
+
+        default:
+            mmi_video_call_hangup_req(TRUE);
+            break;
+    } /* switch */
+
+    voip_signal_check_stop();
+    voip_rat_check_stop();
+
+#if VOIP_PHONE_TEST
+    watch_voip_call_response_no_carrier(voipcall_params.index, 0);
+#endif
+} /* voip_ring_off_action */
+
+lv_voip_phone_obj_ext_t * voip_phone_get_ext(void)
+{
+    lv_voip_phone_obj_ext_t * ext = NULL;
+    lv_obj_t * watch_obj = voip_phone_get_obj();
+
+    if(watch_obj) {
+        ext = lv_obj_get_ext_attr(watch_obj);
+    }
+
+    return(ext);
+}
+
+void * voip_phone_get_video_buffer()
+{
+    return g_cbuf;
+}
+
+/**********************
+*   STATIC FUNCTIONS
+**********************/
+static lv_obj_t * voip_phone_get_activity_obj(void)
+{
+    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_VOIPPHONE);
+
+    return(activity_obj);
+}
+
+static lv_obj_t * voip_phone_get_obj(void)
+{
+    lv_obj_t * watch_obj = NULL;
+
+    lv_obj_t * activity_obj = voip_phone_get_activity_obj();
+
+    if(activity_obj) {
+        lv_watch_get_child_obj(activity_obj, lv_watch_obj_signal, &watch_obj);
+    }
+
+    return(watch_obj);
+}
+
+#if VOIP_PHONE_TEST
+
+static void voip_phone_connected_cb(void * para)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+    watch_voip_call_response_connect(voipcall_params.index);
+}
+#endif
+
+static void voip_phone_left_slide_callback(lv_obj_t * obj)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+#if VOIP_PHONE_TEST
+    if((MMI_VOIP_CALL_STATE_INCOMING == voipcall_params.state)) {   // MT call
+        int32_t index = 1;
+
+        watch_voip_call_response_originating(index);
+        watch_voip_call_response_alerting(index);
+
+        Hal_Timer_Start(60, voip_phone_connected_cb, NULL, 0);
+    }
+
+    else if(MMI_VOIP_CALL_STATE_DIALING == voipcall_params.state) {    // MO call
+        int32_t index = 1;
+
+        watch_voip_call_response_originating(index);
+        watch_voip_call_response_alerting(index);
+
+        Hal_Timer_Start(60, voip_phone_connected_cb, NULL, 0);
+    } else
+#endif
+        if((MMI_VOIP_CALL_STATE_ACTIVE == voipcall_params.state)
+                && (MODEM_VIDEO_CALL_TYPE_VIDEO == voipcall_params.voip_call_type)) {
+            voip_local_remote_switch_action(NULL, LV_EVENT_CLICKED);
+        }
+}
+
+static void voip_phone_right_slide_callback(lv_obj_t * obj)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+    voip_ring_off_action(NULL, LV_EVENT_CLICKED);
+}
+
+static void voip_phone_down_slide_callback(lv_obj_t * obj)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+    voip_volume_btn_action(NULL, LV_EVENT_CLICKED);
+}
+
+static lv_obj_t * voip_phone_cont_create(lv_obj_t * par, lv_align_t cont_layout, lv_style_t * style, lv_coord_t width)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    lv_obj_t * content = lv_cont_create(par, NULL);
+
+    if(width > 0) {
+        lv_cont_set_fit2(content, LV_FIT_NONE, LV_FIT_TIGHT);
+        lv_obj_set_width(content, width);
+    } else {
+        lv_cont_set_fit2(content, LV_FIT_TIGHT, LV_FIT_TIGHT);
+    }
+    lv_obj_add_style(content, LV_OBJ_PART_MAIN, style);
+    lv_cont_set_layout(content, cont_layout);
+    lv_obj_set_click(content, false);
+    return(content);
+}
+
+static void voip_slider_action(lv_obj_t * slider, lv_event_t e)
+{
+    if(e == LV_EVENT_VALUE_CHANGED)
+    {
+        printf("%s %d\n", __FUNCTION__, __LINE__);
+
+        uint8_t v = lv_slider_get_value(slider);
+
+        // set speaker volume
+        if(!Hal_Set_BT_Audio_Path((HAL_SPEAKER_GAIN)v))
+            Hal_Call_Speaker(HAL_CALL_SPK_ON, (HAL_SPEAKER_GAIN)v);
+        voipcall_params.speaker_volume = v;
+    }
+}
+
+static void voip_volume_btn_action(lv_obj_t * obj, lv_event_t e)
+{
+    if(LV_EVENT_CLICKED != e) {
+        return;
+    }
+
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+    slider_create(NULL, SETTING_VOIP_SPEAKER, 40, voip_slider_action);
+}
+
+static void voip_phone_obj_hide_anim_end(lv_anim_t * anim)
+{
+    (void)anim;
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    voip_phone_cleanup();
+}
+
+static lv_obj_t * voip_ring_off_content_create(lv_obj_t * par)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+    if(par == NULL) {
+        par = lv_scr_act();
+    }
+
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return(NULL);
+    }
+
+    lv_obj_t * obj = lv_obj_create(par, NULL);
+    LV_ASSERT_MEM(obj);
+    if(obj == NULL) {
+        return(NULL);
+    }
+    lv_obj_set_size(obj, lv_obj_get_width(par), lv_obj_get_height(par));
+    lv_obj_add_style(obj, LV_OBJ_PART_MAIN, &lv_style_transp_tight);
+
+    lv_obj_t * content = lv_cont_create(obj, NULL);
+    LV_ASSERT_MEM(content);
+    if(content == NULL) {
+        return(NULL);
+    }
+
+    lv_obj_add_style(content, LV_OBJ_PART_MAIN, &lv_watch_cont_opa1);
+    lv_obj_set_size(content, 204, 44);
+    if(ext->phone_ui_t == VOIP_PHONE_UI_INCOMING || ext->phone_ui_t == VOIP_PHONE_UI_DIALING) {
+        lv_obj_align(content, obj, LV_ALIGN_IN_TOP_MID, 0, 10);
+    } else {
+        lv_obj_align(content, obj, LV_ALIGN_CENTER, 0, -12);
+    }
+    lv_cont_set_layout(content, LV_LAYOUT_CENTER);
+    lv_obj_set_click(content, false);
+
+    lv_obj_t * label = lv_label_create(content, NULL);
+    lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+
+    if(is_rat_gsm()) {
+        lv_label_set_text_id(label, WATCH_TEXT_ID_NETWORK_WRONG);
+    } else {
+        if(ext->phone_ui_t == PHONE_UI_CONNECTED) {
+            char tmp[20] = "";
+            char str[30] = "";
+            voip_get_time_elaps(ext->connect_start_time, tmp, 20);
+            snprintf(str, 30, "%s(%s)", (char *)lv_lang_get_text(WATCH_TEXT_ID_RING_OFF), tmp);
+            lv_label_set_text(label, str);
+        } else {
+            lv_label_set_text_id(label, WATCH_TEXT_ID_RING_OFF);
+        }
+    }
+
+    lv_obj_set_click(label, false);
+
+    return(obj);
+} /* voip_ring_off_content_create */
+
+static void voip_phone_ring_off_timeout_cb(void * para)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+    voipcall_params.ring_off_timer_id = NULL;
+    voipcall_params.ring_off_timeout  = true;
+    watch_voip_call_response_no_carrier(voipcall_params.index, 0);
+}
+
+static void voip_ring_hook_action(lv_obj_t * btn, lv_event_t e)
+{
+    if(LV_EVENT_CLICKED != e) {
+        return;
+    }
+
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    if(btn) {
+        lv_obj_set_click(btn, false);
+    }
+
+    Hal_Tone_Play_End();
+
+    int8_t ret = mmi_video_call_answer_req();
+    if(ret < 0) {
+        watch_voip_call_response_no_carrier(voipcall_params.index, 0);
+    }
+}
+
+static void voip_phone_cleanup(void)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    lv_obj_t * slider_activit_obj = lv_watch_get_activity_obj(ACT_ID_SLIDER);
+
+    if(slider_activit_obj) {
+        lv_obj_del(slider_activit_obj);
+    }
+
+    lv_obj_t * activity_obj = voip_phone_get_activity_obj();
+    if(activity_obj) {
+        lv_obj_del(activity_obj);
+    }
+}
+
+static void voip_anim_fp_dialing_ring(lv_obj_t * obj, int32_t x)
+{
+    if(obj != NULL) {
+        lv_obj_set_x(obj, x);
+    }
+}
+
+static void voip_dialing_animation(lv_obj_t * par)
+{
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return;
+    }
+
+    lv_obj_t * img = lv_img_create(voip_phone_get_obj(), NULL);
+    lv_img_set_src(img, LV_SYMBOL_RIGHT);
+    lv_obj_add_style(img, LV_OBJ_PART_MAIN, &lv_watch_image);
+    lv_obj_set_style_local_text_font(img, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_THEME_WATCH_NIGHT_SYMBOL_NORMAL);
+    lv_obj_set_y(img, lv_obj_get_y(par) + lv_obj_get_y(lv_obj_get_parent(par)));
+    ext->phone_animation_obj = img;
+
+    /*Create an animation to move the button continuously left to right*/
+    lv_anim_t a;
+    lv_anim_path_t path;
+    lv_anim_value_t start = lv_obj_get_x(par) + lv_obj_get_width(par) + 6;
+    lv_anim_value_t end = start + 30;
+
+    lv_anim_path_init(&path);
+    lv_anim_path_set_cb(&path, lv_anim_path_linear);
+
+    lv_anim_init(&a);
+    lv_anim_set_var(&a, img);
+    lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)voip_anim_fp_dialing_ring);
+    lv_anim_set_path(&a, &path);
+    lv_anim_set_values(&a, start, end);
+    lv_anim_set_time(&a, 400);
+    lv_anim_set_playback_time(&a, 400);
+    lv_anim_set_playback_delay(&a, 0);
+    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
+    lv_anim_set_repeat_delay(&a, 500);
+
+    lv_anim_start(&a);
+}
+
+static void voip_ring_animation(lv_obj_t * par)
+{
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return;
+    }
+
+    lv_obj_t * img = lv_img_create(voip_phone_get_obj(), NULL);
+    lv_img_set_src(img, LV_SYMBOL_LEFT);
+    lv_obj_add_style(img, LV_OBJ_PART_MAIN, &lv_watch_image);
+    lv_obj_set_style_local_text_font(img, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_THEME_WATCH_NIGHT_SYMBOL_NORMAL);
+    lv_obj_set_y(img, lv_obj_get_y(par) + lv_obj_get_y(lv_obj_get_parent(par)));
+    ext->phone_animation_obj = img;
+
+    /*Create an animation to move the button continuously left to right*/
+    lv_anim_t a;
+    lv_anim_path_t path;
+    lv_anim_value_t start = lv_obj_get_x(par) - 20;
+    lv_anim_value_t end = start - 30;
+
+    lv_anim_path_init(&path);
+    lv_anim_path_set_cb(&path, lv_anim_path_linear);
+
+    lv_anim_init(&a);
+    lv_anim_set_var(&a, img);
+    lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)voip_anim_fp_dialing_ring);
+    lv_anim_set_path(&a, &path);
+    lv_anim_set_values(&a, start, end);
+    lv_anim_set_time(&a, 400);
+    lv_anim_set_playback_time(&a, 400);
+    lv_anim_set_playback_delay(&a, 0);
+    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
+    lv_anim_set_repeat_delay(&a, 500);
+
+    lv_anim_start(&a);
+}
+
+static void voip_connected_animation(lv_obj_t * obj)
+{
+    lv_anim_t a;
+    lv_anim_path_t path;
+
+    lv_anim_path_init(&path);
+    lv_anim_path_set_cb(&path, lv_anim_path_step);
+
+    lv_anim_init(&a);
+    lv_anim_set_var(&a, obj);
+    lv_anim_set_start_cb(&a, (lv_anim_ready_cb_t)voip_anim_fp_connected);
+    lv_anim_set_path(&a, &path);
+    lv_anim_set_time(&a, 1);
+    lv_anim_set_playback_time(&a, 0);
+    lv_anim_set_playback_delay(&a, 0);
+    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
+    lv_anim_set_repeat_delay(&a, 1000);
+
+    lv_anim_start(&a);
+}
+
+static void voip_anim_fp_connected(lv_anim_t * anim)
+{
+    lv_obj_t        *        obj = (lv_obj_t *)anim->var;
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return;
+    }
+
+    char str[20] = "";
+    voip_get_time_elaps(ext->connect_start_time, str, 20);
+    if(obj) {
+        lv_label_set_text(obj, str);
+    }
+}
+
+static void voip_phone_dialing(lv_obj_t * content)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return;
+    }
+
+    lv_obj_t * cont;
+
+    // label dialing
+    lv_obj_t * label = lv_label_create(content, NULL);
+    lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+    lv_label_set_text_id(label, WATCH_TEXT_ID_DAILING);
+    lv_obj_set_click(label, false);
+    ext->phone_status_label = label;
+
+    // img small
+    lv_obj_t * img = lv_img_create(content, NULL);
+    if(voipcall_params.img_src_small) {
+        lv_img_set_src(img, voipcall_params.img_src_small);
+    }
+
+    cont = voip_phone_cont_create(content, LV_LAYOUT_ROW_MID, &lv_watch_style_transp, -1);
+
+    // name
+    label = lv_label_create(cont, NULL);
+    lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+    if(strlen(voipcall_params.name) > 0) {
+        lv_label_set_text(label, voipcall_params.name);
+    } else {
+        lv_label_set_text(label, "");
+    }
+    lv_obj_set_click(label, false);
+
+    // number
+    label = lv_label_create(cont, NULL);
+    lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+    lv_label_set_text(label, voipcall_params.number);
+    lv_obj_set_click(label, false);
+
+    cont = voip_phone_cont_create(content, LV_LAYOUT_OFF, &lv_watch_style_transp, lv_obj_get_width(content) - 40);
+
+    // btn volume
+    lv_obj_t * btn;
+    btn = lv_imgbtn_create(cont, NULL);
+    lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_RING_VOLUME);
+    lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_RING_VOLUME);
+    lv_obj_set_event_cb(btn, voip_volume_btn_action);
+    lv_obj_align(btn, cont, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+
+    // btn ring off
+    btn = lv_imgbtn_create(cont, NULL);
+    lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_RING_OFF);
+    lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_RING_OFF);
+    lv_obj_set_event_cb(btn, voip_ring_off_action);
+    lv_obj_align(btn, cont, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
+} /* voip_phone_dialing */
+
+static void voip_phone_ring(lv_obj_t * content)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return;
+    }
+    lv_obj_t * label;
+
+    // label ringing
+    label = lv_label_create(content, NULL);
+    lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+    lv_label_set_text_id(label, WATCH_TEXT_ID_RINGING);
+    lv_obj_set_click(label, false);
+    ext->phone_status_label = label;
+
+    // img small
+    lv_obj_t * img = lv_img_create(content, NULL);
+    if(voipcall_params.img_src_small) {
+        lv_img_set_src(img, voipcall_params.img_src_small);
+    }
+
+    lv_obj_t * cont = voip_phone_cont_create(content, LV_LAYOUT_ROW_MID, &lv_watch_style_transp, -1);
+
+    // name
+    label = lv_label_create(cont, NULL);
+    lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+    if(strlen(voipcall_params.name) > 0) {
+        lv_label_set_text(label, voipcall_params.name);
+    } else {
+        lv_label_set_text(label, "");
+    }
+    lv_obj_set_click(label, false);
+
+    // number
+    label = lv_label_create(cont, NULL);
+    lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+    lv_label_set_text(label, voipcall_params.number);
+
+    lv_obj_set_click(label, false);
+
+    cont = voip_phone_cont_create(content, LV_LAYOUT_OFF, &lv_watch_style_transp, lv_obj_get_width(content) - 40);
+
+    // btn ring off
+    lv_obj_t * btn;
+    btn = lv_imgbtn_create(cont, NULL);
+    lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_RING_OFF);
+    lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_RING_OFF);
+    lv_obj_set_event_cb(btn, voip_ring_off_action);
+    lv_obj_align(btn, cont, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+
+    // btn ring hook
+    btn = lv_imgbtn_create(cont, NULL);
+    lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_HOOK);
+    lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_HOOK);
+    lv_obj_set_event_cb(btn, voip_ring_hook_action);
+    lv_obj_align(btn, cont, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
+} /* voip_phone_ring */
+
+static void voip_phone_connected(lv_obj_t * content)
+{
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+
+    LV_ASSERT_MEM(ext);
+    if(ext == NULL) {
+        return;
+    }
+
+    if(voipcall_params.voip_call_type == MODEM_VIDEO_CALL_TYPE_VOICE) 
+       {
+        printf("voice call connected %s %d\n", __FUNCTION__, __LINE__);
+        ext->local_remote_switch_flag = VOIP_REMOTE_SWITCH_NULL;
+        // img small
+        lv_obj_t * img = lv_img_create(content, NULL);
+
+        printf("%s: img_src_small %p\n", __FUNCTION__, voipcall_params.img_src_small);
+        if(voipcall_params.img_src_small) {
+            lv_img_set_src(img, voipcall_params.img_src_small);
+        }
+        hal_rtc_t rtc_curr;                    // current rtc time
+        Hal_Rtc_Gettime(&rtc_curr);
+        ext->connect_start_time = time_to_seconds(&rtc_curr);
+
+        lv_obj_t * cont = voip_phone_cont_create(content, LV_LAYOUT_ROW_MID, &lv_watch_style_transp, -1);
+        lv_obj_set_width(cont, lv_obj_get_width(content));
+        lv_obj_add_style(content, LV_OBJ_PART_MAIN, &lv_watch_style_transp);
+        // name
+        lv_obj_t * label = lv_label_create(cont, NULL);
+        lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+        if(strlen(voipcall_params.name) > 0) {
+            lv_label_set_text(label, voipcall_params.name);
+        } else {
+            lv_label_set_text(label, "");
+        }
+        lv_obj_set_click(label, false);
+        lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+
+        // number
+        label = lv_label_create(cont, NULL);
+        lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+        lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+        lv_label_set_text(label, voipcall_params.number);
+        lv_obj_set_click(label, false);
+
+        // duration
+        label = lv_label_create(content, NULL);
+        lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+        lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+        lv_label_set_text(label, "0:0");
+        lv_obj_set_click(label, false);
+        lv_obj_set_base_dir(label, LV_BIDI_DIR_LTR);
+        ext->phone_status_label = label;
+
+        cont = voip_phone_cont_create(content, LV_LAYOUT_OFF, &lv_watch_style_transp, lv_obj_get_width(content) - 40);
+
+        // btn volume
+        lv_obj_t * btn;
+        btn = lv_imgbtn_create(cont, NULL);
+        lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_RING_VOLUME);
+        lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_RING_VOLUME);
+        lv_obj_set_event_cb(btn, voip_volume_btn_action);
+        lv_obj_align(btn, cont, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+
+        // btn ring off
+        btn = lv_imgbtn_create(cont, NULL);
+        lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_RING_OFF);
+        lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_RING_OFF);
+        lv_obj_set_event_cb(btn, voip_ring_off_action);
+        lv_obj_align(btn, cont, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
+    } 
+       else
+       {
+        lv_obj_t * canvas;
+        lv_obj_t * label;
+        lv_obj_t * btn;
+
+        printf("video call connected %s %d\n", __FUNCTION__, __LINE__);
+
+        ext->local_remote_switch_flag = VOIP_REMOTE_SWITCH_REMOTE;
+        if((ext->voiph263fmt == VOIP_H263_FMT_SQCIF) || (ext->voiph263fmt == VOIP_H263_FMT_QCIF)) 
+               {
+            hal_rtc_t rtc_curr;                        // current rtc time
+            Hal_Rtc_Gettime(&rtc_curr);
+            ext->connect_start_time = time_to_seconds(&rtc_curr);
+
+            lv_obj_t * cont = voip_phone_cont_create(content, LV_LAYOUT_OFF, &lv_watch_style_transp, -1);
+            lv_obj_set_width(cont, lv_obj_get_width(content));
+            lv_obj_add_style(content, LV_OBJ_PART_MAIN, &lv_watch_style_transp);
+
+            /*Create the canvas object*/
+            canvas = lv_canvas_create(cont, NULL);
+            lv_obj_set_click(canvas, true);
+            lv_obj_set_event_cb(canvas, canvas_button_hidden_event_cb);
+            video_call_add_element(canvas);
+            ext->canvas = canvas;
+
+            lv_obj_align(canvas, cont, LV_ALIGN_IN_TOP_MID, 0, 0);
+            /*Assign the buffer to the canvas*/
+            lv_img_set_src(canvas, ICON_VIDEO_CALL);
+            voip_signal_check_start(ext);
+
+            // duration
+            label = lv_label_create(canvas, NULL);
+            lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+            lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+            lv_label_set_text(label, "0:0");
+            lv_obj_set_click(label, false);
+            lv_obj_set_base_dir(label, LV_BIDI_DIR_LTR);
+            if(LV_BIDI_DIR_RTL == lv_obj_get_base_dir(content)) {
+                lv_obj_align(label, NULL, LV_ALIGN_IN_TOP_RIGHT, -lv_obj_get_width(label), 0);
+            }
+            ext->phone_status_label = label;
+
+            // signal weak notification
+            label = lv_label_create(canvas, NULL);
+            lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font30);
+            lv_label_set_text_id(label, WATCH_TEXT_ID_SIGNAL_WEAK);
+            lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+            lv_obj_align(label, canvas, LV_ALIGN_CENTER, 0, -10);
+            lv_obj_set_click(label, false);
+            lv_obj_set_hidden(label, true);
+            ext->signal_weak_label = label;
+
+            cont = voip_phone_cont_create(content, LV_LAYOUT_OFF, &lv_watch_style_transp, lv_obj_get_width(content) - 40);
+            // btn volume
+            btn = lv_imgbtn_create(cont, NULL);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_RING_VOLUME);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_RING_VOLUME);
+            lv_obj_set_event_cb(btn, voip_volume_btn_action);
+            lv_obj_align(btn, cont, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+
+            // btn switch video
+            btn = lv_imgbtn_create(cont, NULL);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_SETTING_VOLTE);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_SETTING_VOLTE);
+            lv_obj_set_event_cb(btn, voip_local_remote_switch_action);
+            lv_obj_align(btn, cont, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
+
+            // btn ring off
+            btn = lv_imgbtn_create(cont, NULL);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_RING_OFF);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_RING_OFF);
+            lv_obj_set_event_cb(btn, voip_ring_off_action);
+            lv_obj_align(btn, cont, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
+        } 
+               else if((ext->voiph263fmt == VOIP_H263_FMT_CIF)
+                  || (ext->voiph263fmt == VOIP_H263_FMT_4CIF)
+                  || (ext->voiph263fmt == VOIP_H263_FMT_16CIF)
+                  || (ext->voiph263fmt == VOIP_H263_FMT_OTHER)) 
+               {
+            /*Create the canvas object*/
+            canvas = lv_canvas_create(content, NULL);
+            lv_obj_set_click(canvas, true);
+            lv_obj_set_event_cb(canvas, canvas_button_hidden_event_cb);
+            video_call_add_element(canvas);
+            ext->canvas = canvas;
+
+            /*Assign the buffer to the canvas*/
+            lv_img_set_src(canvas, ICON_VIDEO_CALL);
+            voip_signal_check_start(ext);
+
+            // duration
+            label = lv_label_create(canvas, NULL);
+            lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font20);
+            lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+            lv_label_set_text(label, "0:0");
+            lv_obj_set_click(label, false);
+            lv_obj_set_base_dir(label, LV_BIDI_DIR_LTR);
+            if(LV_BIDI_DIR_RTL == lv_obj_get_base_dir(content)) {
+                lv_obj_align(label, NULL, LV_ALIGN_IN_TOP_RIGHT, -lv_obj_get_width(label), 0);
+            }
+            ext->phone_status_label = label;
+            ext->connect_time       = label;
+
+            // signal weak notification
+            label = lv_label_create(canvas, NULL);
+            lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font30);
+            lv_label_set_text_id(label, WATCH_TEXT_ID_SIGNAL_WEAK);
+            lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+            lv_obj_align(label, canvas, LV_ALIGN_CENTER, 0, -10);
+            lv_obj_set_click(label, false);
+            lv_obj_set_hidden(label, true);
+            ext->signal_weak_label = label;
+
+            // btn volume
+            btn = lv_imgbtn_create(canvas, NULL);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_RING_VOLUME);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_RING_VOLUME);
+            lv_obj_set_event_cb(btn, voip_volume_btn_action);
+            lv_obj_align(btn, canvas, LV_ALIGN_IN_BOTTOM_LEFT, 0, -20);
+            lv_obj_add_style(btn, LV_OBJ_PART_MAIN, &lv_watch_style_transp);
+            ext->ring_value_button = btn;
+
+            // btn switch video
+            btn = lv_imgbtn_create(canvas, NULL);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_SETTING_VOLTE);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_SETTING_VOLTE);
+            lv_obj_set_event_cb(btn, voip_local_remote_switch_action);
+            lv_obj_align(btn, canvas, LV_ALIGN_IN_BOTTOM_MID, 0, -20);
+            lv_obj_add_style(btn, LV_OBJ_PART_MAIN, &lv_watch_style_transp);
+            ext->view_switch_button = btn;
+
+            // btn ring off
+            btn = lv_imgbtn_create(canvas, NULL);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_PHONE_RING_OFF);
+            lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_PHONE_RING_OFF);
+            lv_obj_set_event_cb(btn, voip_ring_off_action);
+            lv_obj_align(btn, canvas, LV_ALIGN_IN_BOTTOM_RIGHT, 0, -20);
+            lv_obj_add_style(btn, LV_OBJ_PART_MAIN, &lv_watch_style_transp);
+            ext->ring_off_button = btn;
+
+            lv_obj_set_hidden(ext->connect_time, ext->canvas_button_hidden_flag);
+            lv_obj_set_hidden(ext->ring_value_button, ext->canvas_button_hidden_flag);
+            lv_obj_set_hidden(ext->view_switch_button, ext->canvas_button_hidden_flag);
+            lv_obj_set_hidden(ext->ring_off_button, ext->canvas_button_hidden_flag);
+        }
+
+        if(voip_phone_get_obj()) {
+            lv_watch_obj_set_down_slide_cb(voip_phone_get_obj(), voip_phone_down_slide_callback);
+        }
+    }
+} /* voip_phone_connected */
+
+static void voip_phone_prepare_destory(lv_obj_t * activity_obj)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    lv_watch_png_cache_all_free();
+
+    // stop playing tone
+    Hal_Tone_Play_End();
+
+    // ring off
+    voip_ring_off_action(NULL, LV_EVENT_CLICKED);
+
+    // stop ring off timer
+    if(NULL != voipcall_params.ring_off_timer_id) {
+        Hal_Timer_Stop(voipcall_params.ring_off_timer_id);
+        voipcall_params.ring_off_timer_id = NULL;
+    }
+
+    // stop callreq timer
+    if(NULL != voipcall_params.callreq_timer_id) {
+        Hal_Timer_Stop(voipcall_params.callreq_timer_id);
+        voipcall_params.callreq_timer_id = NULL;
+    }
+
+    Hal_Mem_Set(&voipcall_params, 0, sizeof(voip_phone_call_params_t));
+    voipcall_params.state = MMI_VOIP_CALL_STATE_NULL;
+}
+
+static void voip_get_time_elaps(uint32_t last_time, char * str, int8_t size)
+{
+    hal_rtc_t rtc_curr;            // current rtc time
+    uint32_t  second_curr = 0;     // seconds of current time
+
+    Hal_Rtc_Gettime(&rtc_curr);
+    second_curr = time_to_seconds(&rtc_curr);
+
+    uint32_t elaps   = second_curr - last_time;
+    uint32_t seconds = elaps % 60;
+    uint32_t minutes = elaps / 60;
+    uint32_t hours   = minutes / 60;
+    minutes = minutes % 60;
+
+    if(hours == 0) {
+        if(lv_lang_act() == LANG_CH) {
+            snprintf(str, size, "%.2d:%.2d", minutes, seconds);
+        } else {
+            snprintf(str, size, "%.2d:%.2d", minutes, seconds);
+        }
+    } else {
+        if(lv_lang_act() == LANG_CH) {
+            snprintf(str, size, "%.2d:%.2d:%.2d", hours, minutes, seconds);
+        } else {
+            snprintf(str, size, "%.2d:%.2d:%.2d", hours, minutes, seconds);
+        }
+    }
+}
+
+#if VOIP_PHONE_TEST != 0
+
+static void watch_voip_call_response_originating(int32_t index)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    if(MMI_VOIP_CALL_STATE_DIALING == voipcall_params.state) {
+        return;
+    }
+
+    printf("%s: %d\n", __func__, index);
+    voipcall_params.index = index;
+
+    // state: NULL -> DIALLING
+    voipcall_params.state = MMI_VOIP_CALL_STATE_DIALING;
+}
+#endif
+
+void watch_voip_call_response_alerting(int32_t index)
+{
+    printf("%s %d index is %d\n", __FUNCTION__, __LINE__, index);
+    if(MMI_VOIP_CALL_STATE_ALERTING == voipcall_params.state) {
+        return;
+    }
+
+    uint8_t volume;
+
+    // state: DIALLING -> ALERTING
+    voipcall_params.state = MMI_VOIP_CALL_STATE_ALERTING;
+
+    // set speaker volume
+    UI_NV_Read_Req(NV_SECTION_UI_PHONE, VOIP_PHONE_NV_VOLUME_OFFSET, 1, &volume);
+    if(!Hal_Set_BT_Audio_Path((HAL_SPEAKER_GAIN)volume))
+        Hal_Call_Speaker(HAL_CALL_SPK_ON, (HAL_SPEAKER_GAIN)volume);
+}
+
+void watch_voip_call_response_ring(int32_t index)
+{
+    printf("%s: %d\n", __func__, index);
+    Hal_NFFS_File_Play_Repeat(AUDIO_INCOMING_CALL, query_current_volume());
+
+    voipcall_params.state = MMI_VOIP_CALL_STATE_ALERTING;
+}
+
+void watch_voip_call_response_connect(int32_t index)
+{
+    printf("%s %d index is %d\n", __FUNCTION__, __LINE__, index);
+    if(MMI_VOIP_CALL_STATE_ACTIVE == voipcall_params.state) {
+        return;
+    }
+
+    voipcall_params.state = MMI_VOIP_CALL_STATE_ACTIVE;
+
+    // stop callreq timer
+    if(NULL != voipcall_params.callreq_timer_id) {
+        Hal_Timer_Stop(voipcall_params.callreq_timer_id);
+        voipcall_params.callreq_timer_id = NULL;
+    }
+
+    // stop playing tone
+    Hal_Tone_Play_End();
+
+    Hal_Set_Voice_Call_Status(VOICE_CALL_START);
+
+    // create connected UI
+    voip_phone_create(NULL, VOIP_PHONE_UI_CONNECTED);
+}
+
+void watch_voip_call_response_disconnect(int32_t index)
+{
+    // not used
+    (void)index;
+    printf("%s: %d\n", __func__, index);
+    Hal_Tone_Play_End();
+
+    // create ring off UI
+    if(voip_phone_get_activity_obj()) {
+        voip_phone_ring_off_create();
+        // remove donw slider handle
+        lv_watch_obj_set_down_slide_cb(voip_phone_get_obj(), NULL);
+    }
+    Hal_Mem_Set(&voipcall_params, 0, sizeof(voip_phone_call_params_t));
+    voipcall_params.state = MMI_VOIP_CALL_STATE_NULL;
+}
+
+void watch_voip_call_response_no_carrier(int32_t index, uint8_t isNoCarrier)
+{
+    printf("%s: %d\n", __func__, __LINE__);
+
+    if((false == voipcall_params.ring_off_timeout)
+            && (1 == isNoCarrier)
+            && (MMI_VOIP_CALL_STATE_NULL == voipcall_params.state)) {
+        printf("%s: ignore no carrier in state NULL\n", __func__);
+        return;
+    }
+
+    printf("%s: %d\n", __func__, index);
+
+    // stop playing tone
+    Hal_Tone_Play_End();
+
+    // stop ring off timer
+    if(NULL != voipcall_params.ring_off_timer_id) {
+        Hal_Timer_Stop(voipcall_params.ring_off_timer_id);
+        voipcall_params.ring_off_timer_id = NULL;
+    }
+
+    // stop callreq timer
+    if(NULL != voipcall_params.callreq_timer_id) {
+        Hal_Timer_Stop(voipcall_params.callreq_timer_id);
+        voipcall_params.callreq_timer_id = NULL;
+    }
+
+    // create ring off UI
+    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_VOIPPHONE);
+    if(activity_obj) {
+        voip_phone_ring_off_create();
+    }
+#if 0
+    // display alarm if it is buffered
+    if(voipcall_params.buffer_alarm) {
+        Hal_NFFS_File_Play_Repeat(AUDIO_ALARM, query_current_volume());
+        alarm_create(NULL);
+    } else {
+        // indicate voice msg
+        voice_msg_call_end();
+    }
+#endif
+    if(MMI_MODEM_CALL_STATE_NULL != phone_get_call_state()) {
+        printf("%s tele call is on going, keep para of audio\n", __FUNCTION__);
+        Hal_Set_Voice_Call_Status(VOICE_CALL_END);
+        Hal_Disconnect_BT_Audio_Path();
+    }
+    Hal_Audio_Manage_End_Ind(AUDIO_CTRL_PRIORITY_2);
+    watch_wakeup_time_reset();
+
+    Hal_Mem_Set(&voipcall_params, 0, sizeof(voip_phone_call_params_t));
+    voipcall_params.state = MMI_VOIP_CALL_STATE_NULL;
+} /* watch_voip_call_response_no_carrier */
+
+void watch_voip_call_response_state_change(MMI_Modem_Call_Response_State_Change_t * state)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+
+    for(uint8_t i = 0; i < state->count; i++) {
+        Hal_Mem_Free(state->calls[i].number);
+        Hal_Mem_Free(state->calls[i].name);
+    }
+    Hal_Mem_Free(state->calls);
+    Hal_Mem_Free(state);
+}
+
+void watch_voip_call_response_bottom_error(void)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+    voip_phone_cleanup();
+    Hal_Mem_Set(&voipcall_params, 0, sizeof(voip_phone_call_params_t));
+}
+
+void watch_voip_call_response_bottom_need_redail(void)
+{
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+    //voip_call_dial_req(voipcall_params.voip_call_type, voipcall_params.number);
+}
+
+void watch_voip_incomingcall_response(char * number, int32_t calltype)
+{
+    uint8_t                  volume;
+    voip_phonebook_contact_t * contact_info = NULL;
+
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: %s,%d\n", __FUNCTION__, __LINE__, number, calltype);
+
+    if((MMI_MODEM_CALL_STATE_NULL != phone_get_call_state()) || is_rat_gsm()) {
+        printf("tele call is on going  %d or in gsm mode, reject voip call\n", phone_get_call_state());
+        mmi_video_call_reject_req(TRUE);
+        return;
+    }
+
+    Hal_Mem_Set(&voipcall_params, 0, sizeof(voip_phone_call_params_t));
+    voipcall_params.voip_call_type = (MODEM_VIDEO_CALL_TYPE)calltype;
+    voipcall_params.is_mt          = true;
+    voipcall_params.state          = MMI_VOIP_CALL_STATE_INCOMING;
+    strncpy(voipcall_params.number, number, NV_CONTACTS_MAX_NUMBER_LEN);
+    voipcall_params.img_src = ICON_FAMILY;
+    voipcall_params.img_src_small = ICON_FAMILY_SMALL;
+       
+    //Hal_Mem_Free(number);
+
+    if(1) 
+       {
+        if(AUDIO_CTRL_PRIORITY_2 == Hal_Audio_Manage_Start_Req(AUDIO_CTRL_PRIORITY_2,voip_audio_ctrl_callback))
+        {
+            watch_set_lcd_status(true);
+            // set speaker volume
+
+            UI_NV_Read_Req(NV_SECTION_UI_PHONE, VOIP_PHONE_NV_VOLUME_OFFSET, 1, &volume);
+            if(!Hal_Set_BT_Audio_Path((HAL_SPEAKER_GAIN)volume))
+                Hal_Call_Speaker(HAL_CALL_SPK_ON, (HAL_SPEAKER_GAIN)volume);
+
+            // play tone
+            Hal_NFFS_File_Play_Repeat(AUDIO_INCOMING_CALL, query_current_volume());
+
+            // start rat checking
+            voip_rat_check_start();
+
+            // create ring UI
+            voip_phone_create(NULL, VOIP_PHONE_UI_INCOMING);
+        }
+        else
+        { // AUDIO_CTRL_PRIORITY_1
+            printf("%s: phone call has been run, auto reject\n", __func__);
+            // auto reject
+            mmi_video_call_reject_req(FALSE);
+            voip_signal_check_stop();
+            voip_rat_check_stop();
+
+            // stop ring off timer
+            if(NULL != voipcall_params.ring_off_timer_id) {
+                Hal_Timer_Stop(voipcall_params.ring_off_timer_id);
+                voipcall_params.ring_off_timer_id = NULL;
+            }
+
+            // stop callreq timer
+            if(NULL != voipcall_params.callreq_timer_id) {
+                Hal_Timer_Stop(voipcall_params.callreq_timer_id);
+                voipcall_params.callreq_timer_id = NULL;
+            }
+
+            Hal_Mem_Set(&voipcall_params, 0, sizeof(voip_phone_call_params_t));
+            voipcall_params.state = MMI_VOIP_CALL_STATE_NULL;
+            voip_set_volume_when_slider_act();
+            voip_phone_cleanup();
+            lv_watch_png_cache_all_free();
+            Hal_Audio_Manage_End_Ind(AUDIO_CTRL_PRIORITY_2);
+        }
+    } else {
+        printf("%s: not a contact, auto reject\n", __func__);
+        // auto reject
+        mmi_video_call_reject_req(TRUE);
+
+#if VOIP_PHONE_TEST
+        watch_voip_call_response_no_carrier(voipcall_params.index, 0);
+#endif
+    }
+} /* watch_voip_incomingcall_response */
+
+void watch_ui_voip_message_handle(void * msg)
+{
+}
+
+void watch_voip_autotest_handle(void * testmsg)
+{
+}
+
+static bool mmi_video_call_check_mo_permission(void)
+{
+    if((!watch_modem_sim_present_check_req())
+               || (MMI_MODEM_SIGNAL_BAR_0 == watch_modem_get_signal_bar_req())
+//       || (MODEM_VIDEO_CALL_REG_OK != watch_modem_video_call_get_reg_stat_req())
+               || (is_rat_gsm())
+               ) 
+       {
+               return false;
+       }
+       
+    return true;
+}
+
+static void voip_local_remote_switch_action(lv_obj_t * btn, lv_event_t e)
+{
+    if(LV_EVENT_CLICKED != e) {
+        return;
+    }
+
+    printf("%s %d\n", __FUNCTION__, __LINE__);
+    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_VOIPPHONE);
+    if(!activity_obj) {
+        return;
+    }
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+    if(!ext) {
+        return;
+    }
+    if(ext->local_remote_switch_flag == VOIP_REMOTE_SWITCH_NULL) {
+        return;
+    }
+    if(ext->local_remote_switch_flag == VOIP_REMOTE_SWITCH_REMOTE) {
+        ext->local_remote_switch_flag = VOIP_REMOTE_SWITCH_LOCAL;
+    } else {
+        ext->local_remote_switch_flag = VOIP_REMOTE_SWITCH_REMOTE;
+    }
+}
+
+static lv_res_t video_call_element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param)
+{
+    lv_res_t res;
+
+    //printf("%s, sign=%d\n", __FUNCTION__, sign);
+    /* if not click signs, invoke ancient handler and return */
+    if(sign != LV_SIGNAL_PRESSING && sign != LV_SIGNAL_PRESSED && sign != LV_SIGNAL_RELEASED && sign != LV_SIGNAL_PRESS_LOST) {
+        /* Include the ancient signal function */
+        lv_signal_cb_t sign_func = (lv_signal_cb_t)(lv_obj_get_user_data(element).user_data);
+        res = sign_func(element, sign, param);
+        if(res != LV_RES_OK) {
+            return(res);
+        }
+
+        return(LV_RES_OK);
+    }
+
+    lv_obj_t * obj = lv_obj_get_parent(element);
+    while(obj) {
+        if(lv_obj_get_signal_cb(obj) != lv_watch_obj_signal) {
+            obj = lv_obj_get_parent(obj);
+        } else {
+            break;
+        }
+    }
+    if(obj == NULL) {
+        return(LV_RES_INV);
+    }
+
+    res = lv_watch_obj_signal(obj, sign, param);
+    if(res != LV_RES_OK) {
+        return(res);
+    }
+
+    /* Include the ancient signal function */
+    lv_signal_cb_t sign_func = (lv_signal_cb_t)(lv_obj_get_user_data(element).user_data);
+    res = sign_func(element, sign, param);
+    if(res != LV_RES_OK) {
+        return(res);
+    }
+
+    return(LV_RES_OK);
+} /* video_call_element_signal_func */
+
+static void video_call_add_element(lv_obj_t * element)
+{
+    lv_watch_set_free_ptr(element, lv_obj_get_signal_cb(element));
+    lv_obj_set_signal_cb(element, video_call_element_signal_func);
+    lv_obj_add_protect(element, LV_PROTECT_PRESS_LOST | LV_PROTECT_CLICK_FOCUS);
+}
+
+static void canvas_button_hidden_event_cb(lv_obj_t * obj, lv_event_t e)
+{
+    if(LV_EVENT_CLICKED == e) 
+       {
+        printf("%s %d\n", __FUNCTION__, __LINE__);
+        lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+        if(!ext) {
+            return;
+        }
+        if(ext->canvas_button_hidden_flag == NOT_HIDDEN) {
+            ext->canvas_button_hidden_flag = HIDDEN;
+        } else {
+            ext->canvas_button_hidden_flag = NOT_HIDDEN;
+        }
+
+        lv_obj_set_hidden(ext->connect_time, ext->canvas_button_hidden_flag);
+        lv_obj_set_hidden(ext->ring_value_button, ext->canvas_button_hidden_flag);
+        lv_obj_set_hidden(ext->view_switch_button, ext->canvas_button_hidden_flag);
+        lv_obj_set_hidden(ext->ring_off_button, ext->canvas_button_hidden_flag);
+    }
+}
+
+static void voip_set_volume_when_slider_act(void)
+{
+    printf("%s\n", __FUNCTION__);
+    if(lv_watch_get_activity_obj(ACT_ID_SLIDER)) {
+        if(MMI_VOIP_CALL_STATE_NULL != voipcall_params.state) {
+            uint8_t volume = voip_get_call_volume();
+            printf("set nv, speaker volume is %d.\n", volume);
+            UI_NV_Write_Req(NV_SECTION_UI_PHONE, 0, 1, &volume);
+        }
+    }
+}
+
+#if VIDEO_SIGNAL_CHECK
+static void signal_check(lv_task_t *task)
+{
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+    if(!ext) return;
+
+    if((false == is_rat_gsm()) &&
+       (watch_modem_get_signal_bar_req() < MMI_MODEM_SIGNAL_BAR_3)) {
+        lv_obj_set_hidden(ext->signal_weak_label, false);
+    } else {
+        lv_obj_set_hidden(ext->signal_weak_label, true);
+    }
+}
+#endif
+
+static void voip_signal_check_start(void * ext)
+{
+#if VIDEO_SIGNAL_CHECK
+    if(NULL == signal_check_task)
+        signal_check_task = lv_task_create(signal_check, VIDEO_SIGNAL_CHECK_PERIOD, LV_TASK_PRIO_HIGH, NULL);
+#endif
+}
+
+static void voip_signal_check_stop(void)
+{
+#if VIDEO_SIGNAL_CHECK
+    if(signal_check_task) {
+        lv_task_del(signal_check_task);
+        signal_check_task = NULL;
+    }
+#endif
+}
+
+static bool is_rat_gsm(void)
+{
+    MMI_MODEM_PLMN_RAT rat;
+
+    watch_modem_get_operator_req(&rat);
+    if(MMI_MODEM_PLMN_RAT_GSM == rat)
+        return true;
+    else
+        return false;
+}
+
+static void rat_check(lv_task_t *task)
+{
+    if(is_rat_gsm()) {
+        voip_ring_off_action(NULL, LV_EVENT_CLICKED);
+    }
+}
+
+static void voip_rat_check_start(void)
+{
+    if(NULL == rat_check_task)
+        rat_check_task = lv_task_create(rat_check, VIDEO_RAT_CHECK_PERIOD, LV_TASK_PRIO_HIGH, NULL);
+}
+
+static void voip_rat_check_stop(void)
+{
+    if(rat_check_task) {
+        lv_task_del(rat_check_task);
+        rat_check_task = NULL;
+    }
+}
+
+void voip_fps_check(int8_t fps)
+{
+    lv_voip_phone_obj_ext_t * ext = voip_phone_get_ext();
+    if(NULL != ext) {
+        if((VIDEO_CALL_FPS_CHECK_LIMIT >= fps) && (0 != fps)) {
+            printf("%s %d\n", __FUNCTION__, fps);
+            if(lv_obj_get_hidden(ext->signal_weak_label) == false) return;
+            lv_obj_set_hidden(ext->signal_weak_label, false);
+        } else {
+            if(lv_obj_get_hidden(ext->signal_weak_label) == true) return;
+            lv_obj_set_hidden(ext->signal_weak_label, true);
+        }
+    }
+}
+
+
+
+#define VOIP_ROOM_ID "jrtc012" // "104799"
+
+static uint8_t s_video_call_init_flag = 0;
+static TIMER_ID fail_timer_id = NULL;
+
+static void mmi_video_call_req_timeout_cb(void * para)
+{
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+
+    voipcall_params.callreq_timer_id = NULL;
+    mmi_video_call_hangup_req(TRUE);
+}
+
+void mmi_video_call_req(char * number, MODEM_VIDEO_CALL_TYPE voip_call_type)
+{
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: %s,%d\n", __FUNCTION__, __LINE__, number, voip_call_type);
+
+    // check call state
+    if(MMI_VOIP_CALL_STATE_NULL != voipcall_params.state)
+       {
+               VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: %d\n", __FUNCTION__, __LINE__, voipcall_params.state);
+        return;
+    }
+
+    // stop ring off timer
+    if(NULL != call_params.ring_off_timer_id) 
+       {
+        Hal_Timer_Stop(call_params.ring_off_timer_id);
+        call_params.ring_off_timer_id = NULL;
+    }
+
+    // stop callreq timer
+    if(NULL != voipcall_params.callreq_timer_id) 
+       {
+        Hal_Timer_Stop(voipcall_params.callreq_timer_id);
+        voipcall_params.callreq_timer_id = NULL;
+    }
+
+    // set call parameters
+    Hal_Mem_Set(&voipcall_params, 0, sizeof(voip_phone_call_params_t));
+    voipcall_params.is_mt          = false;
+    voipcall_params.state          = MMI_VOIP_CALL_STATE_DIALING;
+    voipcall_params.voip_call_type = voip_call_type;
+    strcpy(voipcall_params.number, number);
+    voipcall_params.img_src       = ICON_FAMILY;
+    voipcall_params.img_src_small = ICON_FAMILY_SMALL;
+
+    int8_t ret = mmi_video_call_dial_req(voip_call_type, voipcall_params.number);
+    if(NULL == voipcall_params.callreq_timer_id) 
+       {
+               UINT32 timeout = (ret >= 0) ? VOIP_CALLREQ_TIME_OUT : 1*1000;
+               
+        voipcall_params.callreq_timer_id = Hal_Timer_Start(timeout, mmi_video_call_req_timeout_cb, NULL, 0);
+    }
+    // create dialing UI
+    voip_rat_check_start();
+    voip_phone_create(NULL, VOIP_PHONE_UI_DIALING);
+    Hal_Audio_Manage_Start_Req(AUDIO_CTRL_PRIORITY_2, voip_audio_ctrl_callback);
+    Hal_Set_Voice_Call_Status(VOICE_CALL_START);
+}
+
+static void mmi_video_call_main_prepare_destory(lv_obj_t * activity_obj)
+{
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+       
+    lv_watch_png_cache_all_free();
+
+       if(NULL != fail_timer_id)
+       {
+               Hal_Timer_Stop(fail_timer_id);
+               fail_timer_id = NULL;
+       }
+}
+
+void mmi_video_call_fail_timeout_cb(void *para)
+{
+       mmi_video_call_main_prepare_destory(NULL);
+}
+
+static void mmi_video_call_fail(lv_obj_t * obj)
+{
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+
+       lv_obj_t * cont = lv_cont_create(obj, NULL);
+       lv_cont_set_fit(cont, LV_FIT_NONE);
+       lv_obj_set_size(cont, lv_obj_get_width(obj), lv_obj_get_height(obj));
+       lv_obj_set_style_local_bg_color(cont, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+       lv_cont_set_layout(cont, LV_LAYOUT_OFF);
+       lv_obj_set_click(cont, false);
+
+       lv_obj_t * label = lv_label_create(cont, NULL);
+       lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font26); // &lv_watch_font20);
+       lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+       lv_obj_set_click(label, false);
+       lv_label_set_text_id(label, WATCH_TEXT_ID_NO_SIM_OR_NETWORK); // WATCH_TEXT_ID_NO_CONTACTS_OR_NETWORK);
+       lv_obj_align(label, cont, LV_ALIGN_CENTER, 0, 0);
+
+       TIMER_ID timer_id = Hal_Timer_Start(2*1000, mmi_video_call_fail_timeout_cb, NULL, FALSE);
+}
+
+static void mmi_video_call_voice_btn_action(lv_obj_t * btn, lv_event_t e)
+{
+    if(LV_EVENT_CLICKED != e)
+       {
+        return;
+    }
+
+    if(mmi_video_call_check_mo_permission())
+       {
+        mmi_video_call_req(VOIP_ROOM_ID, MODEM_VIDEO_CALL_TYPE_VOICE);
+    } 
+       else 
+       {
+        mmi_video_call_fail(lv_watch_get_activity_obj(ACT_ID_VOIP_PHONEBOOK));
+    }
+}
+
+static void mmi_video_call_voideo_btn_action(lv_obj_t * btn, lv_event_t e)
+{
+    if(LV_EVENT_CLICKED != e)
+       {
+        return;
+    }
+
+    if(mmi_video_call_check_mo_permission()) 
+       {
+        mmi_video_call_req(VOIP_ROOM_ID, MODEM_VIDEO_CALL_TYPE_VIDEO);
+    } 
+       else 
+       {
+        mmi_video_call_fail(lv_watch_get_activity_obj(ACT_ID_VOIP_PHONEBOOK));
+    }
+}
+
+static lv_obj_t * mmi_video_call_main_create(lv_obj_t * activity_obj)
+{
+    if(activity_obj == NULL) 
+       {
+        lv_watch_activity_ext_t activity_ext = {0};
+
+        activity_ext.actId = ACT_ID_VOIP_PHONEBOOK;
+        activity_ext.create = mmi_video_call_main_create;
+        activity_ext.prepare_destory = mmi_video_call_main_prepare_destory;
+        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
+        if(NULL == activity_obj)
+            return NULL;
+    }
+
+    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
+    if(NULL == obj)
+        return NULL;
+
+    if(false == mmi_video_call_check_mo_permission()) 
+       {
+               VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+               
+               lv_obj_t * label = lv_label_create(obj, NULL);
+               
+               lv_obj_add_style(label, LV_OBJ_PART_MAIN, &lv_watch_font26); // &lv_watch_font20);
+               lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
+               lv_obj_set_click(label, false);
+               lv_label_set_text_id(label, WATCH_TEXT_ID_NO_SIM_OR_NETWORK); // WATCH_TEXT_ID_NO_CONTACTS_OR_NETWORK);
+               lv_obj_align(label, obj, LV_ALIGN_CENTER, 0, 0);
+               
+        //lv_watch_set_free_num(activity_obj, 0);
+    } 
+       else
+       {
+               VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+               
+               lv_obj_t * cont = lv_cont_create(obj, NULL);
+               lv_cont_set_fit(cont, LV_FIT_NONE);
+               lv_obj_set_size(cont, lv_obj_get_width(obj), lv_obj_get_height(obj));
+               lv_obj_set_style_local_bg_color(cont, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+               lv_cont_set_layout(cont, LV_LAYOUT_OFF);
+               lv_obj_set_click(cont, false);
+
+               lv_obj_t * voicebtn = lv_imgbtn_create(cont, NULL);
+               lv_imgbtn_set_src(voicebtn, LV_BTN_STATE_RELEASED, ICON_VIDEO_CALL_VOICE_BTN);
+               lv_imgbtn_set_src(voicebtn, LV_BTN_STATE_PRESSED, ICON_VIDEO_CALL_VOICE_BTN);
+               lv_obj_align(voicebtn, cont, LV_ALIGN_IN_TOP_MID, 0, LV_VER_RES / 4);
+               lv_obj_set_event_cb(voicebtn, mmi_video_call_voice_btn_action);
+               
+               lv_obj_t * videobtn = lv_imgbtn_create(cont, NULL);
+               lv_imgbtn_set_src(voicebtn, LV_BTN_STATE_RELEASED, ICON_VIDEO_CALL_VIDEO_BTN);
+               lv_imgbtn_set_src(voicebtn, LV_BTN_STATE_PRESSED, ICON_VIDEO_CALL_VIDEO_BTN);
+               lv_obj_align(videobtn, cont, LV_ALIGN_IN_BOTTOM_MID, 0, -LV_VER_RES / 4);
+               lv_obj_set_event_cb(videobtn, mmi_video_call_voideo_btn_action);
+
+               //lv_watch_set_free_num(activity_obj, 1);
+    }
+
+    return(obj);
+}
+
+void video_call_create_event_cb(lv_obj_t * btn, lv_event_t e)
+{
+    if(LV_EVENT_CLICKED == e) 
+       {
+               if(0 == s_video_call_init_flag)
+               {
+                       s_video_call_init_flag = 1;
+                       mmi_video_call_jrtc_config_init();
+               }
+               
+        lv_watch_png_cache_all_free();
+        mmi_video_call_main_create(NULL);
+    }
+    else if(LV_EVENT_LONG_PRESSED == e)
+       {
+
+       }
+}
+
+#endif
+
+//interface
+uint8_t voip_get_call_volume(void)
+{
+#ifdef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+    return(voipcall_params.speaker_volume);
+#endif
+
+    return 0;
+}
+
+bool watch_voip_suspend_check(void)
+{
+#ifdef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+    if((voipcall_params.voip_call_type == MODEM_VIDEO_CALL_TYPE_VIDEO)
+               || (MMI_VOIP_CALL_STATE_INCOMING == voipcall_params.state)
+               || (MMI_VOIP_CALL_STATE_DIALING == voipcall_params.state)
+               ) 
+    {
+        return false;
+    }
+#endif
+
+    return true;
+}
+
+bool voip_connect_check_req(void)
+{
+#ifdef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+    if(voip_phone_get_activity_obj()) 
+       {
+        return true;
+    }
+#endif
+
+       return false;
+}
+
+void voip_activity_destroy_req(void)
+{
+#ifdef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+       VIDEO_CALL_DEBUG_PRINTF("[@CHENG][%s][%d]: \n", __FUNCTION__, __LINE__);
+
+    lv_obj_t * activity_obj = voip_phone_get_activity_obj();
+    if(NULL == activity_obj) 
+       {
+        return;
+    }
+
+    switch(voipcall_params.state) 
+       {
+        case MMI_VOIP_CALL_STATE_NULL:
+        case MMI_VOIP_CALL_STATE_INVALID:
+            // do noting
+            break;
+
+        case MMI_VOIP_CALL_STATE_ACTIVE:
+            mmi_video_call_hangup_req(FALSE);
+            break;
+
+        case MMI_VOIP_CALL_STATE_INCOMING:
+            Hal_Tone_Play_End();
+            mmi_video_call_reject_req(FALSE);
+            break;
+
+        default:
+            mmi_video_call_hangup_req(FALSE);
+            break;
+    }
+
+    voip_signal_check_stop();
+    voip_rat_check_stop();
+
+    // stop ring off timer
+    if(NULL != voipcall_params.ring_off_timer_id) {
+        Hal_Timer_Stop(voipcall_params.ring_off_timer_id);
+        voipcall_params.ring_off_timer_id = NULL;
+    }
+
+    // stop callreq timer
+    if(NULL != voipcall_params.callreq_timer_id) {
+        Hal_Timer_Stop(voipcall_params.callreq_timer_id);
+        voipcall_params.callreq_timer_id = NULL;
+    }
+
+    Hal_Mem_Set(&voipcall_params, 0, sizeof(voip_phone_call_params_t));
+    voipcall_params.state = MMI_VOIP_CALL_STATE_NULL;
+    voip_set_volume_when_slider_act();
+    voip_phone_cleanup();
+    lv_watch_png_cache_all_free();
+#endif
+}
+
+
diff --git a/2.sdk/gui/lv_watch/lv_apps/mmi_video_call/mmi_video_call.h b/2.sdk/gui/lv_watch/lv_apps/mmi_video_call/mmi_video_call.h
new file mode 100644 (file)
index 0000000..44346e8
--- /dev/null
@@ -0,0 +1,221 @@
+/**
+ * @file videocall.h
+ *
+ */
+
+#ifndef VIDEOCALL_H
+#define VIDEOCALL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lvgl.h"
+#include "lv_ex_conf.h"
+#else
+#include "../../../lvgl/lvgl.h"
+#include "../../../lv_ex_conf.h"
+#endif
+
+#if 0
+void voip_alarm_ind(void);
+#endif
+bool voip_connect_check_req(void);
+bool watch_voip_suspend_check(void);
+void voip_activity_destroy_req(void);
+uint8_t voip_get_call_volume(void);
+
+
+#ifdef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+
+#define __VIDEO_CALL_DEBUG__
+#ifdef __VIDEO_CALL_DEBUG__
+#define VIDEO_CALL_DEBUG_PRINTF(fmt, ...)      printf("[VIDEO_CALL]"fmt, ##__VA_ARGS__)
+#else
+#define VIDEO_CALL_DEBUG_PRINTF(fmt, ...)
+#endif
+
+
+#define ENABLE_VIDEO_SCALE  1
+#define VOIP_PHONE_TEST  0
+#define VOIP_PHONEBOOK_TEST 0
+#define VOIP_JUPHOON_TEST 0 // 1
+#define VOIP_PHONE_RING_OFF_ANIMATION_TIME 0
+#define VOIP_PHONE_RING_OFF_CONTINUE_TIME 1500   /*ms*/
+#define VOIP_PHONE_NV_VOLUME_OFFSET          0
+#define VOIP_PHONE_NV_EMERGENCY_NUM_OFFSET   1
+#define VOIP_PHONEBOOK_CONTACTS_MAX_NAME_LEN    NV_CONTACTS_MAX_NAME_LEN
+#define VOIP_PHONEBOOK_CONTACTS_MAX_NUMBER_LEN  NV_CONTACTS_MAX_NUMBER_LEN
+#define LV_COLOR_HEX3(c) LV_COLOR_MAKE((uint8_t) (((c >> 4) & 0xF0) | ((c >> 8) & 0xF)),   \
+                                (uint8_t) ((uint32_t)(c & 0xF0)     | ((c & 0xF0) >> 4)), \
+                                (uint8_t) ((uint32_t)(c & 0xF)      | ((c & 0xF) << 4)))
+#define H263_FMT_QCIF 1
+#if H263_FMT_QCIF == 1
+#define VIDEO_SOURCE_WIDTH   176
+#define VIDEO_SOURCE_HEIGHT  144
+#endif
+
+#if (ENABLE_VIDEO_SCALE==0)
+#define canvas_width  VIDEO_SOURCE_WIDTH
+#define canvas_heigh  VIDEO_SOURCE_HEIGHT
+#else
+#define canvas_width  LV_HOR_RES   /* 240 */
+#define canvas_heigh  LV_VER_RES   /* 240 */
+#endif
+
+#define VIDEO_RENDER_PERIOD  50   /* 1000/50=20fps */
+#define VIDEO_SIGNAL_CHECK  0
+#define VIDEO_SIGNAL_CHECK_PERIOD  1000
+#define VIDEO_RAT_CHECK_PERIOD  200
+#define VIDEO_CALL_FPS_CHECK_LIMIT 5
+
+#define VOIP_CALLREQ_TIME_OUT    60*1000
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef enum {
+    VOIP_PHONE_UI_INCOMING = 0,
+    VOIP_PHONE_UI_DIALING,
+    VOIP_PHONE_UI_CONNECTED,
+} voip_phone_ui_e;
+
+typedef enum
+{
+    MMI_VOIP_CALL_STATE_NULL     = 0,
+    MMI_VOIP_CALL_STATE_ACTIVE   = 1,
+    MMI_VOIP_CALL_STATE_HOLDING  = 2,
+    MMI_VOIP_CALL_STATE_DIALING  = 3,                                           /* MO call only */
+    MMI_VOIP_CALL_STATE_ALERTING = 4,                                           /* MO call only */
+    MMI_VOIP_CALL_STATE_INCOMING = 5,                                           /* MT call only */
+    MMI_VOIP_CALL_STATE_WAITING  = 6,                                           /* MT call only */
+    MMI_VOIP_CALL_STATE_INVALID  = 0xFF
+} MMI_VOIP_CALL_STATE_E;
+
+enum voipregisterstate
+{
+    VOIP_REGISTER_OK = 0,
+    VOIP_UNREGISTERING,
+    VOIP_REGISTER_FAIL
+};
+
+/** H.263 picture size format */
+enum voip_h263_fmt {
+    VOIP_H263_FMT_SQCIF = 1, /**<  128 x 96  */
+    VOIP_H263_FMT_QCIF  = 2, /**<  176 x 144  */
+    VOIP_H263_FMT_CIF   = 3, /**<  352 x 288  */
+    VOIP_H263_FMT_4CIF  = 4, /**<  704 x 576  */
+    VOIP_H263_FMT_16CIF = 5, /**< 1408 x 1152 */
+    VOIP_H263_FMT_OTHER = 7,
+};
+
+enum local_remote_switch_e {
+    VOIP_REMOTE_SWITCH_REMOTE = 0,
+    VOIP_REMOTE_SWITCH_LOCAL,
+    VOIP_REMOTE_SWITCH_NULL,
+};
+
+enum enum_canvas_button_hidden_flag
+{
+    NOT_HIDDEN,
+    HIDDEN
+};
+
+typedef struct {
+    char contact_name[VOIP_PHONEBOOK_CONTACTS_MAX_NAME_LEN];
+    char contact_number[VOIP_PHONEBOOK_CONTACTS_MAX_NUMBER_LEN];
+    const void * img_src;
+    const void * img_src_small;
+    uint8_t contact_type; /*0:administrator, 1:standby administrator, 2: others*/
+} voip_phonebook_contact_t;
+
+typedef nv_watch_phonebook_info_t voip_phonebook_t;
+
+typedef struct {
+    lv_watch_obj_ext_t lv_watch_obj;
+    /*New data for this type */
+    uint8_t body_padding_inner;
+    uint8_t append_width;
+    uint8_t cur_img_id;
+    voip_phonebook_t * cur_contact;
+    lv_ll_t contact_list;
+    lv_obj_t * label_phone_contact;
+    lv_obj_t * page_scrl;
+    lv_coord_t move_width;
+} lv_voip_phonebook_obj_ext_t;
+
+typedef struct {
+    MMI_VOIP_CALL_STATE_E state;
+    int32_t              index;                    /* Connection Index for use with, eg, AT+CHLD */
+    bool                 is_mt;                    /* true if MT call */
+    bool                 ring_off;                 /* true if ring off by user */
+    bool                 ring_off_timeout;         /* true if ring off timeout */
+    bool                 buffer_alarm;
+#define VOIP_RING_OFF_TIMER_LEN    1000
+    TIMER_ID             ring_off_timer_id;        /* ring off timer */
+    char                 number[NV_CONTACTS_MAX_NUMBER_LEN];   /* phone number */
+    char                 name[NV_CONTACTS_MAX_NAME_LEN];       /* contact name */
+    const void * img_src;
+    const void * img_src_small;
+    MODEM_VIDEO_CALL_TYPE    voip_call_type;
+    TIMER_ID             callreq_timer_id;         /* callreq timer */
+    uint8_t              speaker_volume;           /* speaker volume during call procedure */
+} voip_phone_call_params_t;
+
+typedef struct {
+    lv_watch_obj_ext_t lv_watch_obj;
+    /*New data for this type */
+    lv_obj_t * phone_status_label;
+    lv_obj_t * phone_animation_obj;
+    uint32_t connect_start_time;
+    voip_phone_ui_e phone_ui_t;
+    enum local_remote_switch_e local_remote_switch_flag;
+    enum voip_h263_fmt voiph263fmt;
+    lv_obj_t * connect_time;
+    lv_obj_t * ring_value_button;
+    lv_obj_t * view_switch_button;
+    lv_obj_t * ring_off_button;
+    enum enum_canvas_button_hidden_flag canvas_button_hidden_flag;
+    lv_obj_t * signal_weak_label;
+    lv_obj_t * canvas;
+} lv_voip_phone_obj_ext_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+lv_voip_phone_obj_ext_t * voip_phone_get_ext(void);
+void * voip_phone_get_video_buffer(void);
+void watch_voip_call_response_connect(int32_t index);
+void watch_voip_call_response_no_carrier(int32_t index, uint8_t isNoCarrier);
+void watch_voip_call_response_alerting(int32_t index);
+void watch_voip_incomingcall_response(char * number, int32_t calltype);
+void watch_voip_call_response_bottom_error(void);
+void watch_voip_call_response_bottom_need_redail(void);
+bool check_voipcall_enter_pm_ind(void);
+void watch_voip_message_handle_init(void);
+void watch_ui_voip_message_handle(void * voipmsg);
+void watch_voip_autotest_handle(void * testmsg);
+
+void mmi_video_call_jrtc_config_init(void);
+int8_t mmi_video_call_dial_req(MODEM_VIDEO_CALL_TYPE calltype, char * pContactUri);
+void mmi_video_call_hangup_req(BOOL bye);
+int8_t mmi_video_call_answer_req(void);
+void mmi_video_call_reject_req(BOOL bye);
+void voip_fps_check(int8_t fps);
+
+
+void video_call_create_event_cb(lv_obj_t *btn, lv_event_t e);
+
+#endif
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*VIDEOCALL_H*/
index 858cd33..b106e5d 100644 (file)
@@ -30,10 +30,10 @@ static lv_obj_t * menu_add_btn(lv_obj_t * tab, int8_t menu_index, int8_t btn_id,
 #endif
 static void primary_set_default_menu(nv_watch_launcher_t * launcher_info);
 
-#if USE_LV_WATCH_VIDEOCALL == 0
-void videocallmain_create(lv_obj_t * btn, lv_event_t e)
-{}
-#endif
+//#if USE_LV_WATCH_VIDEOCALL == 0
+//void videocallmain_create(lv_obj_t * btn, lv_event_t e)
+//{}
+//#endif
 #if USE_LV_WATCH_QRCODE_SCAN == 0
 void qrcode_scan_create_event_cb(lv_obj_t * btn, lv_event_t event)
 {}
@@ -95,7 +95,9 @@ static watch_app_info_t app[] = {
     { ICON_CALCULATOR, WATCH_TEXT_ID_CALCULATOR, calculator_create_event_cb },
     { ICON_SPORT, WATCH_TEXT_ID_SPORT, sport_create_event_cb },
     { ICON_HEART_RATE, WATCH_TEXT_ID_HEART_RATE, heart_rate_create_event_cb},
+#if USE_LV_WATCH_VIDEOCALL != 0
     { ICON_VIDEOCALL, WATCH_TEXT_ID_VIDEOCALL, videocallmain_create },
+#endif
     { ICON_AIKNOWLEDGE, WATCH_TEXT_ID_AIKNOWLEGE, btn_event_cb },
     { ICON_CALENDAR, WATCH_TEXT_ID_CALENDER, calendar_create_event_cb },
     { ICON_BLOODPRESSURE, WATCH_TEXT_ID_BLOODPRESSURE, btn_event_cb },
index 182d22b..60adfd5 100644 (file)
@@ -37,7 +37,9 @@ static watch_app_info_t s_primary_grid_menu[] =
 #if USE_LV_WATCH_ALIPAY != 0
        { ICON_ALIPAY, WATCH_TEXT_ID_ALIPAY, upay_create_event_cb },
 #endif
-       { ICON_VIDEOCALL, WATCH_TEXT_ID_VIDEOCALL, videocallmain_create },
+#ifdef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+       { ICON_VIDEOCALL, WATCH_TEXT_ID_VIDEOCALL, video_call_create_event_cb },
+#endif
 
 #if USE_LV_WATCH_AUDIO_PLAYER != 0
        { ICON_MUSIC, WATCH_TEXT_ID_AUDIO_PLAYER, audio_player_create_event_cb },
index 1ab3e38..4c4d1cc 100644 (file)
@@ -93,7 +93,7 @@ extern "C" {
 #include "lv_apps/voice_msg/voice_msg.h"
 #include "lv_apps/speech_recog/speech_recog.h"
 #include "lv_apps/album/album.h"
-#include "lv_apps/video_call/video_call.h"
+//#include "lv_apps/video_call/video_call.h"
 
 #include "lv_apps/restart_and_reset/restart_and_reset.h"
 #include "lv_apps/audio_player/audio_player.h"
@@ -174,8 +174,9 @@ extern "C" {
 #include "mmi_protocol/mmi_protocol_fzd.h"
 #include "mmi_protocol/mmi_protocol_sms.h"
 #include "mmi_sundry/mmi_sundry.h"
+#include "lv_apps/mmi_video_call/mmi_video_call.h"
 #ifdef __MMI_WECHAT_SUPPORT__
-#include "lv_apps/wechat/wechat.h"
+#include "lv_apps/mmi_wechat/wechat.h"
 #endif
 #include "lv_apps/health_measure/health_measure.h"
 #ifdef __MMI_HEALTH_MEASURE_HEART_SUPPORT__
index e18c302..62c3b3b 100644 (file)
@@ -676,6 +676,13 @@ LV_IMG_DECLARE(img_poweron_logo);
 #define ICON_COMMON_BG_WHITE                         "R:/png/common/ICON_COMMON_BG_WHITE.png"
 #define ICON_COMMON_SUCCESS                          "R:/png/common/ICON_COMMON_SUCCESS.png"
 
+//video call
+#ifdef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+//#define ANIM_HEART_RATE_MEASURING                                            "R:/gif/anim_heart_rate_measuring.gif"
+#define ICON_VIDEO_CALL_VOICE_BTN                      "R:/png/video_call/ICON_VIDEO_CALL_VOICE_BTN.png"
+#define ICON_VIDEO_CALL_VIDEO_BTN                      "R:/png/video_call/ICON_VIDEO_CALL_VIDEO_BTN.png"
+#endif
+
 // wechat
 #ifdef __MMI_WECHAT_SUPPORT__
 #define ICON_WECHAT_NO_NETWORK                          "R:/png/wechat/ICON_WECHAT_NO_NETWORK.png"
diff --git a/2.sdk/gui/lv_watch/resource/png/video_call/ICON_VIDEO_CALL_VIDEO_BTN.png b/2.sdk/gui/lv_watch/resource/png/video_call/ICON_VIDEO_CALL_VIDEO_BTN.png
new file mode 100644 (file)
index 0000000..7042249
Binary files /dev/null and b/2.sdk/gui/lv_watch/resource/png/video_call/ICON_VIDEO_CALL_VIDEO_BTN.png differ
diff --git a/2.sdk/gui/lv_watch/resource/png/video_call/ICON_VIDEO_CALL_VOICE_BTN.png b/2.sdk/gui/lv_watch/resource/png/video_call/ICON_VIDEO_CALL_VOICE_BTN.png
new file mode 100644 (file)
index 0000000..db6e3cf
Binary files /dev/null and b/2.sdk/gui/lv_watch/resource/png/video_call/ICON_VIDEO_CALL_VOICE_BTN.png differ
index 5ff3608..2d81479 100644 (file)
@@ -75,7 +75,7 @@
 #define USE_LV_WATCH_ENGINEER_MODE 1
 #define USE_LV_WATCH_CTA 1
 #define USE_LV_WATCH_VOLTE_SWTICH 0 // 1
-#define USE_LV_WATCH_VIDEOCALL 1
+#define USE_LV_WATCH_VIDEOCALL 0 // 1
 #define USE_LV_WATCH_CALENDAR 1
 #define USE_LV_WATCH_AUDIO_PLAYER 0 // 1
 #define USE_LV_WATCH_IMGVIEWER 1
 
 #define __MMI_ZHSX_SUPPORT_ALIPAY__
 
+#define __MMI_ZHSX_SUPPORT_VIDEO_CALL__
+
+
 #if 1 //def MMI_WECHAT_SUPPORT
 #define __MMI_WECHAT_SUPPORT__
 #endif
index 1fe66d8..90db401 100644 (file)
@@ -5073,12 +5073,14 @@ typedef struct
     kal_uint8  error_state;
 } mmi_jrtc_video_call_error_ind_struct;
 
+#if 0 // ndef __MMI_ZHSX_SUPPORT_VIDEO_CALL__
 typedef struct
 {
     LOCAL_PARA_HDR
        kal_uint8       name[JRTC_NAME_MAX_LENGTH];
        kal_uint8       uid_dst[JRTC_UID_MAX_LENGTH];
 } mmi_jrtc_video_call_ring_ind_struct;
+#endif
 
 /*CSD:END*/
 
index b980bde..1094c3a 100644 (file)
@@ -4,67 +4,60 @@
 extern "C" {
 #endif
 
-#define JRTC_APPKEY_MAX_LENGTH 30
-#define JRTC_KEY_MAX_LENGTH 20
-#define JRTC_SRVURL_MAX_LENGTH 60
-#define JRTC_UID_MAX_LENGTH 10
-#define JRTC_UID_DST_MAX_LENGTH 10
-#define JRTC_NAME_MAX_LENGTH 14
-
 /**
- * 呼叫流程: 假设 `设备A' 呼叫 `设备B'
+ * ºô½ÐÁ÷³Ì: ¼ÙÉè `É豸A' ºô½Ð `É豸B'
  *
- * 启动连接流程
- * - A 需要 "某种推送机制" (长连接,推送方式) 通知到B,
- * - A 打开通信(jrtc_open), 初始状态为 JRTC_OPENED
- * - B 收到 "该种推送通知" 后也打开通信(jrtc_open), 初始状态为 JRTC_OPENED
- * - A 主动激活媒体设备,以便自动接听
+ * Æô¶¯Á¬½ÓÁ÷³Ì
+ * - A ÐèÒª "ijÖÖÍÆËÍ»úÖÆ" (³¤Á¬½Ó,ÍÆËÍ·½Ê½) Í¨Öªµ½B,
+ * - A ´ò¿ªÍ¨ÐÅ(jrtc_open), ³õʼ״̬Ϊ JRTC_OPENED
+ * - B ÊÕµ½ "¸ÃÖÖÍÆËÍ֪ͨ" ºóÒ²´ò¿ªÍ¨ÐÅ(jrtc_open), ³õʼ״̬Ϊ JRTC_OPENED
+ * - A Ö÷¶¯¼¤»îýÌåÉ豸,ÒÔ±ã×Ô¶¯½ÓÌý
  *          jrtc_activate(jc, JRTC_AUDIO|JRTC_CAMERA)
  *
- * 成功建立网络通道
- * - A 和 B 在服务器上对呼后连通, 通话状态都变为 JRTC_JOINED
- * - A 可以开始渲染镜头预览(渲染流程)
+ * ³É¹¦½¨Á¢ÍøÂçͨµÀ
+ * - A ºÍ B ÔÚ·þÎñÆ÷É϶ԺôºóÁ¬Í¨, Í¨»°×´Ì¬¶¼±äΪ JRTC_JOINED
+ * - A ¿ÉÒÔ¿ªÊ¼äÖȾ¾µÍ·Ô¤ÀÀ(äÖȾÁ÷³Ì)
  *
- * 被叫流程
- * - B 收到A的音视频数据, 通话状态变为 JRTC_TALKING
- * - B 可以开始渲染对端视频(渲染流程)
- * - B 弹出接听提示, 若用户选择"拒接"则进入挂断流程
- * - B 在界面选择接听后, 也激活媒体设备
+ * ±»½ÐÁ÷³Ì
+ * - B ÊÕµ½AµÄÒôÊÓÆµÊý¾Ý, Í¨»°×´Ì¬±äΪ JRTC_TALKING
+ * - B ¿ÉÒÔ¿ªÊ¼äÖȾ¶Ô¶ËÊÓÆµ(äÖȾÁ÷³Ì)
+ * - B µ¯³ö½ÓÌýÌáʾ, ÈôÓû§Ñ¡Ôñ"¾Ü½Ó"Ôò½øÈë¹Ò¶ÏÁ÷³Ì
+ * - B ÔÚ½çÃæÑ¡Ôñ½ÓÌýºó, Ò²¼¤»îýÌåÉ豸
  *          jrtc_activate(jc,JRTC_AUDIO|JRTC_CAMERA)
  *
- * 主叫流程
- * - A 在收到音视频数据后, 状态变为JRTC_TALKING
- * - A 也开始渲染对端视频(渲染流程)
- *
- * 挂断流程
- * - A 调用 jrtc_close 主动挂断
- * - B 通话状态将变为 JRTC_LEAVING 或 JRTC_CLOSED
- * - 通过 jrtc_error() 可以获取挂断的原因(出错值)
- * - B 调用 jrtc_close 结束通话
- *
- * 最简函数流程
- * - jrtc_config(...);   // 配置环境,只需调用一次
- * - jc = jrtc_open(...);// 打开通话,返回对象
- * - jrtc_activate(jc,JRTC_AUDIO|JRTC_CAMERA);//激活设备
- * - 在 UI 线程,更新视频或预览,参考下面的`渲染流程'
- * - jrtc_close(jc, JRTC_EBYE); //主动挂断, 并且等待网络线程结束
- *
- * 收到来电中断的处理
- * - jrtc_close(jc, JRTC_EAUDIOCALL);//结束通话,等待网络线程结束
+ * Ö÷½ÐÁ÷³Ì
+ * - A ÔÚÊÕµ½ÒôÊÓÆµÊý¾Ýºó, ×´Ì¬±äΪJRTC_TALKING
+ * - A Ò²¿ªÊ¼äÖȾ¶Ô¶ËÊÓÆµ(äÖȾÁ÷³Ì)
+ *
+ * ¹Ò¶ÏÁ÷³Ì
+ * - A µ÷Óàjrtc_close Ö÷¶¯¹Ò¶Ï
+ * - B Í¨»°×´Ì¬½«±äΪ JRTC_LEAVING »ò JRTC_CLOSED
+ * - Í¨¹ý jrtc_error() ¿ÉÒÔ»ñÈ¡¹Ò¶ÏµÄÔ­Òò(³ö´íÖµ)
+ * - B µ÷Óàjrtc_close ½áÊøÍ¨»°
+ *
+ * ×î¼òº¯ÊýÁ÷³Ì
+ * - jrtc_config(...);   // ÅäÖû·¾³,Ö»Ðèµ÷ÓÃÒ»´Î
+ * - jc = jrtc_open(...);// ´ò¿ªÍ¨»°,·µ»Ø¶ÔÏó
+ * - jrtc_activate(jc,JRTC_AUDIO|JRTC_CAMERA);//¼¤»îÉ豸
+ * - ÔÚ UI Ïß³Ì,¸üÐÂÊÓÆµ»òÔ¤ÀÀ,²Î¿¼ÏÂÃæµÄ`äÖȾÁ÷³Ì'
+ * - jrtc_close(jc, JRTC_EBYE); //Ö÷¶¯¹Ò¶Ï, ²¢ÇҵȴýÍøÂçÏ߳̽áÊø
+ *
+ * ÊÕµ½À´µçÖжϵĴ¦Àí
+ * - jrtc_close(jc, JRTC_EAUDIOCALL);//½áÊøÍ¨»°,µÈ´ýÍøÂçÏ߳̽áÊø
  */
 
-/** 通话状态 */
+/** Í¨»°×´Ì¬ */
 enum jrtc_state
 {
-    JRTC_CLOSED=0,/*< 初始状态 */
-    JRTC_LEAVING, /*< jrtc_leave 或内部出错后的状态 */
-    JRTC_OPENED,  /*< 调用jrtc_open 后的状态 */
-    JRTC_JOINING, /*< 加入中的状态 */
-    JRTC_JOINED,  /*< 成功加入会话 */
-    JRTC_TALKING, /*< 收到媒体后的状态 */
+    JRTC_CLOSED=0,/*< ³õʼ״̬ */
+    JRTC_LEAVING, /*< jrtc_leave »òÄÚ²¿³ö´íºóµÄ״̬ */
+    JRTC_OPENED,  /*< µ÷ÓÃjrtc_open ºóµÄ״̬ */
+    JRTC_JOINING, /*< ¼ÓÈëÖеÄ״̬ */
+    JRTC_JOINED,  /*< ³É¹¦¼ÓÈë»á»° */
+    JRTC_TALKING, /*< ÊÕµ½Ã½ÌåºóµÄ״̬ */
 };
 
-/** 通话错误值 */
+/** Í¨»°´íÎóÖµ */
 enum jrtc_error
 {
     JRTC_ENIL   = 0,  /* unknown error */
@@ -75,6 +68,8 @@ enum jrtc_error
     JRTC_ECONNRESET=104, /* Connection reset by peer */
     JRTC_ETIMEDOUT =101, /* Connection timed out */
     JRTC_ECONNREFUSED=111,/* Connection refused or closed */
+
+    JRTC_EOFFLINE = 128, /* Peer offline or over */
     JRTC_EDECLINE = 129, /* Peer decline the call */
     JRTC_EINVOKETIMEOUT = 130, /* IoTUnit invoke timeout */
     JRTC_ECALLSERVERERROR = 131, /* Call server error */
@@ -82,13 +77,19 @@ enum jrtc_error
     JRTC_ECALLTIMEOUT = 133, /* Call timeout */
     JRTC_ENOTREG = 134, /* device id not registered */
     JRTC_ELOGIN = 135, /* device id login failed */
+    JRTC_ETOKEN_NOTFOUND = 136, /* secret not found */
+    JRTC_ETOKEN_INV = 137, /* token invalid */
+    JRTC_ETOKEN_EXPIRE = 138, /* token expire */
+    JRTC_ECODECINVALID = 139, /* Codec params invalid */
+
     JRTC_EAUDIOCALL = 252, /* external audio call, don't close audio deivce */
     JRTC_EAUDIO=253,  /* Audio device error */
     JRTC_EVIDEO=254,  /* Video device error */
+
     JRTC_EBYE = 255,  /* Success, Closed by peer leave */
 };
 
-/** 通话中的媒体设备 */
+/** Í¨»°ÖеÄýÌåÉ豸 */
 enum jrtc_device
 {
     JRTC_SPEAKER    = 1,
@@ -97,112 +98,199 @@ enum jrtc_device
     JRTC_AUDIO      = 3,
 };
 
-/** 渲染流程:
- * jrtc_image_t* img 代表 jrtc_open 中传入 video 或 camera
- * - 检测当前为通话状态, 否则无须渲染
+enum jrtc_netstat
+{
+    JRTC_NET_NORMAL  = 0, /* network is normal or unknown */
+    JRTC_LOCAL_POOR  = 1,
+    JRTC_REMOTE_POOR = 2,
+};
+
+/** Êý¾ÝÀàÐÍ
+ * @note
+ * |   Èí¼þϵͳ   |   Æ½Ì¨   |  ²É¼¯¸ñʽ  |       ±àÂëºó¸ñʽ     |    ½âÂëºó¸ñʽ    |
+ * | ------------ | -------- | ---------- | -------------------- | ---------------- |
+ * | ASR Crane    | 3601     | JRTC_NV12  | JRTC_H263,JRTC_JPEG  | JRTC_I420,JRTC_RGB565 |
+ */
+enum jrtc_dtype
+{
+    JRTC_I420 = 0, /*< Í¼Ïñ×Ö½ÚÊý=W*H*3/2 */
+    JRTC_NV12 = 1, /*< Í¼Ïñ×Ö½ÚÊý=W*H*3/2 */
+    JRTC_UYVY = 2, /*< Í¼Ïñ×Ö½ÚÊý=W*H*2 */
+    JRTC_JPEG = 3, /*< ±£³Ö¼æÈÝÐÔ */
+    JRTC_NV16 = 4, /*< Í¼Ïñ×Ö½ÚÊý=W*H*2 */
+
+    JRTC_RGB565 = 64,
+
+    JRTC_H264 = 96,
+    JRTC_H263 = 97,
+};
+
+/** äÖȾÁ÷³Ì:
+ * jrtc_image_t* img ´ú±í jrtc_open Öд«Èë video »ò camera
+ * - ¼ì²âµ±Ç°ÎªÍ¨»°×´Ì¬, ·ñÔòÎÞÐëäÖȾ
  *   if (jrtc_state(jc) < JRTC_JOINED) break;
  *
- * - 内部自动更新图像:
+ * - ÄÚ²¿×Ô¶¯¸üÐÂͼÏñ:
  *      if (img->get == img->put) {
- *          '更新图像内容: 即对img中的data, width, height, format 赋值'
- *          img->put++;//增加写计数
+ *          '¸üÐÂͼÏñÄÚÈÝ: ¼´¶ÔimgÖеÄdata, width, height, format ¸³Öµ'
+ *          img->put++;//Ôö¼Óд¼ÆÊý
  *      }
  *
- * - 上层UI线程(App 界面层)负责绘制图像:
+ * - ÉϲãUIÏß³Ì(App ½çÃæ²ã)¸ºÔð»æÖÆÍ¼Ïñ:
  *      if (img->get != img->put) {
- *          '界面绘制操作: 即使用img中的data, 更新界面控件'
- *          img->get = img->put;//增加读计数
+ *          '½çÃæ»æÖƲÙ×÷: ¼´Ê¹ÓÃimgÖеÄdata, ¸üнçÃæ¿Ø¼þ'
+ *          img->get = img->put;//Ôö¼Ó¶Á¼ÆÊý
  *      }
- *   在 ASR原生 LittlevGL 的 lv_watch 界面中, 界面绘制操作:
- *      // yuv420_2_rgb565 是内置的格式转换函数
+ *   ÔÚ ASRÔ­Éú LittlevGL µÄ lv_watch ½çÃæÖÐ, ½çÃæ»æÖƲÙ×÷:
+ *      // yuv420_2_rgb565 ÊÇÄÚÖõĸñʽת»»º¯Êý
  *      //extern void yuv420_2_rgb565(int width, int height, const unsigned char *src, unsigned short *dst, unsigned semi);
  *      yuv420_2_rgb565(img->width, img->height, img->data, img_dsc->data, img->format);
- *      lv_img_set_src(img, img_dsc);//img, img_dsc 都是界面中的相应UI控件,
+ *      lv_img_set_src(img, img_dsc);//img, img_dsc ¶¼ÊǽçÃæÖеÄÏàÓ¦UI¿Ø¼þ,
  *
  *
- * 在jrtc_open()中用于设置建议值, 通话过程中用于获取图像数据.
- * 例如, 希望接收和发送都是176x144 8帧/秒, 120 kbps 的音视频通话:
+ * ÔÚjrtc_open()ÖÐÓÃÓÚÉèÖý¨ÒéÖµ, Í¨»°¹ý³ÌÖÐÓÃÓÚ»ñȡͼÏñÊý¾Ý.
+ * ÀýÈç, Ï£Íû½ÓÊպͷ¢ËͶ¼ÊÇ176x144 8Ö¡/Ãë, 120 kbps µÄÒôÊÓÆµÍ¨»°:
  *   static struct jrtc_image_t video, camera;
  *   video.width  = camera.width  = 176;
  *   video.height = camera.height = 144;
  *   video.fps    = camera.fps    = 8;
  *   video.kbps   = camera.kbps   = 120;
- *   //注意,以上仅是建议值, 在最终接通时, 由服务器决定实际值
- *
- *   jc = jrtc_open(roomid, 0, &video, &camera);
+ *   //×¢Òâ,ÒÔÉϽöÊǽ¨ÒéÖµ, ÔÚ×îÖÕ½Óͨʱ, ÓÉ·þÎñÆ÷¾ö¶¨Êµ¼ÊÖµ
+ *   //ĿǰÄÚ²¿Ö§³ÖJPEG, H263. ×îÖÕÓÉÅäÖÃÓë¶Ô¶ËЭÉ̾ö¶¨¡£
+ *   // H263: Ö»Ö§³Ö176x144ºÍ352x288Á½ÖÖ×ö·Ö±æÂÊ
+ *   // JPEG: ÒªÇó´ø¿íkbps > width * height * 8 * fps / 10000
+ *   jc = jrtc_open(roomid, NULL, &video, &camera);
  */
 struct jrtc_image_t
-{
+{/* ¿ÉǿתΪ jrtc_photo_t Ê¹Óà*/
     void* data;
-    unsigned short width, height, kbps;
+    unsigned bytes;
+    unsigned short width, height;
+    char format;/*< enum jrtc_dtype */
     char fps;
-    char format;/*< 0: I420; 1: NV12 */
+    unsigned short kbps;
     volatile char put, get;
+    char reserve[2];
 };
 
-/** 当前的版本号 */
-#define JRTC_VERTION   "1.4.0"
+/** µ±Ç°µÄ°æ±¾ºÅ */
+#define JRTC_VERSION   "1.7.0.0"
 
-/** 配置全局参数,只需调用一次.
- * 注意: 内部直接引用地址, 由外部保证内存的有效性.
+/** ÅäÖÃÈ«¾Ö²ÎÊý,Ö»Ðèµ÷ÓÃÒ»´Î.
+ * ×¢Òâ:
+ * - appkey ¹Ì¶¨³¤¶ÈΪ24
+ * - key ¹Ì¶¨³¤¶ÈΪ16
+ * - uid ÏÞÖÆ×î´ó×Ö·ûÊýΪ64
+ * - tokens ÏÞÖÆ×î´ó×Ö·ûÊýΪ128
  *
- * @param[in] appkey 当前APP特有的标识, 需向Juphoon申请获得
- * @param[in] uid 账户
- * @param[in] srvURL 服务器地址
- * @param[in] key 通讯密钥, 必须与appkey匹配, 需向Juphoon申请或注册
+ * @param[in] appkey µ±Ç°APPÌØÓеıêʶ, ÐèÏòJuphoonÉêÇë»ñµÃ
+ * @param[in] uid ÕË»§
+ * @param[in] key Í¨Ñ¶ÃÜÔ¿, ±ØÐëÓëappkeyÆ¥Åä, ÐèÏòJuphoonÉêÇë»ò×¢²á
+ * @param[in] ÓÃÓÚ¼øÈ¨µÄtokens,ĬÈÏΪ""
  */
-void jrtc_config (const char* appkey, const char* uid, const char* srvURL, const char key[16]);
+void jrtc_config (const char* appkey, const char* uid, const char key[16], const char* tokens);
 
-/** 分配通话资源并启动网络线程
- * 注意:
- * - 内部直接引用地址,由外部保证内存的有效性
- * - 内部设备初始没有激活,后续需要调如下开启
+/** ·ÖÅäͨ»°×ÊÔ´²¢Æô¶¯ÍøÂçÏß³Ì
+ * ×¢Òâ:
+ * - ¶Ôticket³¤¶ÈºÍ²ÎÊýroomid¶¼ÏÞÖÆ×î´ó×Ö·ûÊýΪ64
+ * - ÄÚ²¿É豸³õʼûÓ줻î,ºóÐøÐèÒªµ÷ÈçÏ¿ªÆô
  *   jrtc_activate(jc,JRTC_AUDIO|JRTC_CAMERA)
  *
- * ticket 作为通话ID, 唯一标识每次通话:
- *  - 不能包含JSON需转义的字符
- *  - 每次通话, 要使用不同的 ticket,(随机产生或向服务器申请)
- *  - A 呼叫前,申请获得 ticket
- *  - A 需要通"某种推送机制"(长连接,推送方式) 将 ticket 通知到B
- *  - A 和B 对呼时, 必须使用相同的ticket, 以便服务器精确标识会话
- *
- * @param[in] roomid    å\8a å\85¥ç\9a\84æ\88¿é\97´å\8f·(对æ\96¹è´¦æ\88·)
- * @param[in] ticket    用于房间的hash值,可通过其他路径获得,默认为NULL,仅用于调试.
- * @param[in] video     期望接收的视频尺寸和帧速,码率.  若 NULL,则不接收视频
- * @param[in] camera    用于预览图像,建议的发送尺寸和帧速,码率.  若 NULL, 则不使用镜头.
- *                      最终的视频尺寸和镜头的尺寸,都由服务器协商决定.
+ * ticket ×÷Ϊͨ»°ID, Î¨Ò»±êʶÿ´Îͨ»°:
+ *  - ²»Äܰüº¬JSONÐèתÒåµÄ×Ö·û
+ *  - Ã¿´Îͨ»°, ÒªÊ¹Óò»Í¬µÄ ticket,(Ëæ»ú²úÉú»òÏò·þÎñÆ÷ÉêÇë)
+ *  - A ºô½Ðǰ,ÉêÇë»ñµÃ ticket
+ *  - A ÐèҪͨ"ijÖÖÍÆËÍ»úÖÆ"(³¤Á¬½Ó,ÍÆËÍ·½Ê½) ½« ticket Í¨Öªµ½B
+ *  - A ºÍB ¶Ôºôʱ, ±ØÐëʹÓÃÏàͬµÄticket, ÒÔ±ã·þÎñÆ÷¾«È·±êʶ»á»°
+ *
+ * @param[in] id            ¼ÓÈëÒ»¶ÔÒ»»á»°(¶Ô·½µÄuid)»ò·¿¼ä(ÒÔroom:ǰ׺µÄ·¿¼äµØÖ·)
+ * @param[in] ticket        hashÖµ,¿Éͨ¹ýÆäËû·¾¶»ñµÃ,ĬÈÏΪNULL,½öÓÃÓÚµ÷ÊÔ.
+ * @param[in,out] video     ÆÚÍû½ÓÊÕµÄÊÓÆµ³ß´çºÍÖ¡ËÙ,ÂëÂÊ.  Èô NULL,Ôò²»½ÓÊÕÊÓÆµ
+ * @param[in,out] camera    ÓÃÓÚÔ¤ÀÀͼÏñ,½¨ÒéµÄ·¢ËͳߴçºÍÖ¡ËÙ,ÂëÂÊ.  Èô NULL, Ôò²»Ê¹ÓþµÍ·.
+ *                          ×îÖÕµÄÊÓÆµ³ß´çºÍ¾µÍ·µÄ³ß´ç,¶¼ÓÉ·þÎñÆ÷ЭÉ̾ö¶¨.
  */
-struct jrtc_t* jrtc_open (const char* roomid, const char* ticket, struct jrtc_image_t* video, struct jrtc_image_t* camera);
+struct jrtc_t* jrtc_open (const char* id, const char* ticket, struct jrtc_image_t* video, struct jrtc_image_t* camera);
 
-/** 释放通话资源,并等待网络线程结束.
+/** ÊÍ·Åͨ»°×ÊÔ´,²¢µÈ´ýÍøÂçÏ߳̽áÊø.
  *
- * @param[in] err   主动挂断为 JRTC_EBYE
- *                  但当语音电话中断时, 须设置为JRTC_EAUDIOCALL,
- *                  其他未知情况,可设置为 JRTC_ENIL(不修改挂断原因)
+ * @param[in] err   Ö÷¶¯¹Ò¶ÏΪ JRTC_EBYE
+ *                  µ«µ±ÓïÒôµç»°ÖжÏʱ, ÐëÉèÖÃΪJRTC_EAUDIOCALL,
+ *                  ÆäËûδ֪Çé¿ö,¿ÉÉèÖÃΪ JRTC_ENIL(²»Ð޸ĹҶÏÔ­Òò)
  */
 void jrtc_close (struct jrtc_t* jc, enum jrtc_error err);
 
-/** 动态设置前后镜头
- * @param[in] camera 0为后镜头,1是前镜头
+/** ±àÂëǰµÄµÄÊÓÆµÖ¡ */
+struct jrtc_photo_t
+{
+    void* data;
+    unsigned bytes;
+    unsigned short width,height;
+    char format;/*< enum jrtc_dtype */
+    char reserve[3];
+};
+
+/** ¶¯Ì¬ÉèÖÃǰºó¾µÍ·
+ * @param[in] camera 0Ϊºó¾µÍ·,1ÊÇǰ¾µÍ·
+ * @param[in] effect ±àÂëǰͼÏñµÄ´¦Àí»Øµ÷
+ *                   ½ûÖ¹¸Ä±äͼÏñ³ß´çºÍ¸ñʽ.
+ *                   ¿ÉÉèÖÃΪ NULL
+ * @code 
+ *   static char* encoding = NULL;
+ *   //Ö»¾µÏñ·¢ËÍͼÏñ
+ *   jrtc_set_camera(jc, 1, [](struct jrtc_photo_t* frame){
+ *      auto preview = frame->data;
+ *      if (!encoding) encoding = new char[frame->bytes];
+ *
+ *      assert (frame->format == JRTC_NV12 && "¸ù¾Ýƽ̨ѡÔñת»»º¯Êý");
+ *      jrtc_nv12_mirror(frame, frame->width, frame->height, encoding);
+ *      frame->data = encoding;
+ *   });
+ *   //Ö»¾µÏñÔ¤ÀÀͼÏñ
+ *   jrtc_set_camera(jc, 1, [](struct jrtc_photo_t* frame){
+ *      auto preview = frame->data;
+ *      if (!encoding) encoding = new char[frame->bytes];
+ *      frame->data = memcpy(encoding, preview, frame->bytes);
+ *
+ *      assert (frame->format == JRTC_NV12 && "¸ù¾Ýƽ̨ѡÔñת»»º¯Êý");
+ *      jrtc_nv12_mirror(frame, frame->width, frame->height, preview);
+ *   });
+ *   //ͬʱ¾µÏñÔ¤ÀÀºÍ·¢ËÍͼÏñ
+ *   jrtc_set_camera(jc, 1, [](struct jrtc_photo_t* frame){
+ *      auto preview = frame->data;
+ *      if (!encoding) encoding = new char[frame->bytes];
+ *      frame->data = memcpy(encoding, preview, frame->bytes);
+ *
+ *      assert (frame->format == JRTC_NV12 && "¸ù¾Ýƽ̨ѡÔñת»»º¯Êý");
+ *      jrtc_nv12_mirror(frame, frame->width, frame->height, preview);
+ *      frame->data = preview;
+ *   });
+ * @endcode
  */
-void jrtc_set_camera (struct jrtc_t* jc, int camera);
+void jrtc_set_camera (struct jrtc_t* jc, int camera,
+    void (*effect) (struct jrtc_photo_t* frame));
 
-/** 激活设备, 开启发送能力
- * @param[in] devices jrtc_device 的集合
+/** ¼¤»îÉ豸, ¿ªÆô·¢ËÍÄÜÁ¦
+ * @param[in] devices jrtc_device µÄ¼¯ºÏ
  */
 void jrtc_activate (struct jrtc_t* jc, int devices);
 
-/** 禁活设备, 关闭发送能力
- * @param[in] devices jrtc_device 的集合
+/** ½û»îÉ豸, ¹Ø±Õ·¢ËÍÄÜÁ¦
+ * @param[in] devices jrtc_device µÄ¼¯ºÏ
  */
 void jrtc_deactivate (struct jrtc_t* jc, int devices);
 
-/** 返回通话状态 */
+/** ·µ»ØÍ¨»°×´Ì¬ */
 enum jrtc_state jrtc_state  (const struct jrtc_t *jc);
 
-/** 返回最近的出错值 */
+/** ·µ»Ø×î½üµÄ³ö´íÖµ */
 enum jrtc_error jrtc_error  (const struct jrtc_t *jc);
 
+/** ·µ»ØÍøÂç״̬
+ * @param[out] remote_active_devices  ¶Ô¶ËµÄµÄÊý¾ÝÁ÷״̬. jrtc_device µÄ¼¯ºÏ
+ *                                    ÔÊÐí NULL.
+ */
+enum jrtc_netstat jrtc_netstat(struct jrtc_t* jc, int *remote_active_devices);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/2.sdk/product/craneg_modem/framework/inc/jrtc0.h b/2.sdk/product/craneg_modem/framework/inc/jrtc0.h
new file mode 100644 (file)
index 0000000..18dff25
--- /dev/null
@@ -0,0 +1,154 @@
+#ifndef __JRTC_0_H__
+#define __JRTC_0_H__
+#include "jrtc.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Í¨»°×´Ì¬
+ *               jrtc_alloc              jrtc_schedule               jrtc_schedule
+ * JRTC_CLOSED ----------> JRTC_OPENED -------------> JRTC_JOINING -------------> JRTC_JOINED
+ *       ^                                                                            |
+ *       | jrtc_step                                                                  | jrtc_schedule 
+ *       |            jrtc_leave »ò ÄÚ²¿³ö´í                                          | ÊÕµ½Ã½ÌåÊý¾Ý
+ * JRTC_LEAVING <---------------------------+------------- JRTC_TALKING <-------------+
+ *                                          |                  ^
+ *                                          |  jrtc_schedule   |
+ *                                          +------------------+
+ */
+
+/** ÖØÖ÷þÎñÆ÷µØÖ·
+ * ×¢Òâ:
+ * - ÊÖ¹¤ÉèÖÃеØÖ·,ͨ³£Ö»ÓÃÓÚ²âÊÔ
+ * - srvURL ÏÞÖÆ×î´ó×Ö·ûÊýΪ128
+ * - srvURL Îª¿Õʱ, »Ö¸´ÎªÄÚÖõÄĬÈÏ·þÎñÆ÷µØÖ·
+ *
+ * @param[in] srvURL ·þÎñÆ÷µØÖ· 
+ */
+void jrtc_config_server (const char* srvURL);
+
+/** ·ÖÅäͨ»°×ÊÔ´
+ * ×¢Òâ:
+ * - ÄÚ²¿Ö±½ÓÒýÓõØÖ·,ÓÉÍⲿ±£Ö¤ÄÚ´æµÄÓÐЧÐÔ
+ * - ÄÚ²¿Ã»ÓÐÆô¶¯ÍøÂçÏß³Ì,ÐèÒª²»¶ÏµÄµ÷ÓÃÈçÏÂÇý¶¯
+ *   jrtc_schedule(jc, nowMs);
+ * - ÄÚ²¿É豸³õʼûÓ줻î,ºóÐøÐèÒªµ÷ÈçÏ¿ªÆô
+ *   jrtc_activate(jc,JRTC_AUDIO|JRTC_CAMERA)
+ *
+ * ticket ×÷Ϊͨ»°ID, Î¨Ò»±êʶÿ´Îͨ»°:
+ *  - ²»Äܰüº¬JSONÐèתÒåµÄ×Ö·û
+ *  - Ã¿´Îͨ»°, ÒªÊ¹Óò»Í¬µÄ ticket,(Ëæ»ú²úÉú»òÏò·þÎñÆ÷ÉêÇë)
+ *  - A ºô½Ðǰ,ÉêÇë»ñµÃ ticket
+ *  - A ÐèҪͨ"ijÖÖÍÆËÍ»úÖÆ"(³¤Á¬½Ó,ÍÆËÍ·½Ê½) ½« ticket Í¨Öªµ½B
+ *  - A ºÍB ¶Ôºôʱ, ±ØÐëʹÓÃÏàͬµÄticket, ÒÔ±ã·þÎñÆ÷¾«È·±êʶ»á»°
+ *
+ * @param[in] roomid    ¼ÓÈëµÄ·¿¼äºÅ(¶Ô·½ÕË»§)
+ * @param[in] ticket    ÓÃÓÚ·¿¼äµÄhashÖµ,¿Éͨ¹ýÆäËû·¾¶»ñµÃ,ĬÈÏΪNULL,½öÓÃÓÚµ÷ÊÔ.
+ * @param[in] video     ÆÚÍû½ÓÊÕµÄÊÓÆµ³ß´çºÍÖ¡ËÙ,ÂëÂÊ.  Èô NULL,Ôò²»½ÓÊÕÊÓÆµ
+ * @param[in] camera    ÓÃÓÚÔ¤ÀÀͼÏñ,½¨ÒéµÄ·¢ËͳߴçºÍÖ¡ËÙ,ÂëÂÊ.  Èô NULL, Ôò²»Ê¹ÓþµÍ·.
+ *                      ×îÖÕµÄÊÓÆµ³ß´çºÍ¾µÍ·µÄ³ß´ç,¶¼ÓÉ·þÎñÆ÷ЭÉ̾ö¶¨.
+ */
+struct jrtc_t* jrtc_alloc (const char* roomid, const char* ticket, struct jrtc_image_t* video, struct jrtc_image_t* camera);
+
+/** µ¥²½¸üÐÂͨ»°
+ * ÒªÇóʵʱÐÔ´¦Àí, Îñ±ØÔÚ¶ÀÁ¢Ïß³ÌÖгÖÐø¸üÐÂ, 
+ * µ±·µ»Ø JRTC_CLOSED Ê±, ¿ÉÒÔÍ˳ö¸ÃÏß³Ì.
+ * ÀýÈç:
+ *  Æô¶¯ÍøÂçÏß³Ì([jc]{
+ *      do {
+ *          state = jrtc_schedule(jc, µ±Ç°ºÁÃë);
+ *      } while(state != JRTC_CLOSED);
+ *  }
+ *
+ * @param[in] ms        µ±Ç°ºÁÃë
+ */
+enum jrtc_state jrtc_schedule (struct jrtc_t* jc, unsigned ms);
+
+/** Ö÷¶¯À뿪/¹Ò¶Ï
+ * ×¢Òâ: ÕâÀïÖ»¸Ä±ä״̬, Ïú»Ù½«ÔÚjrtc_schedule()ÖÐ×Ô¶¯´¦Àí
+ * 
+ * @param[in] err   Ö÷¶¯¹Ò¶ÏΪ JRTC_EBYE
+ *                  µ«µ±ÓïÒôµç»°ÖжÏʱ, ÐëÉèÖÃΪJRTC_EAUDIOCALL,
+ *                  ÆäËûδ֪Çé¿ö,¿ÉÉèÖÃΪ JRTC_ENIL(²»Ð޸ĹҶÏÔ­Òò)
+ */
+enum jrtc_state jrtc_leave (struct jrtc_t *jc, enum jrtc_error err);
+
+/** ¾µÏñ
+ * @param[in] dst_w Ä¿±ê¿í¶È, »á×Ô¶¯²Ã¼ô,µ«²»Ëõ·Å
+ * @param[in] dst_h Ä¿±ê¸ß¶È, ¸ºÊýʱ»áÏÈÉÏÏ·­×ª(µÈ¼ÛÓÚÐýת180¶È)
+ * @param[out] dst  Ä¿±êÄÚ´æ
+ */
+void jrtc_i420_mirror(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+void jrtc_nv12_mirror(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+void jrtc_uyvy_mirror(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+void jrtc_nv16_mirror(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+
+/** Ë³Ê±Õëת90¶È
+ * @param[in] dst_w Ä¿±ê¿í¶È, »á×Ô¶¯²Ã¼ô,µ«²»Ëõ·Å
+ * @param[in] dst_h Ä¿±ê¸ß¶È, ¸ºÊýʱ»áÏÈÉÏÏ·­×ª(µÈ¼ÛÓÚת90ºóˮƽ¾µÏñ)
+ * @param[out] dst  Ä¿±êÄÚ´æ
+ */
+void jrtc_i420_rotate90(const struct jrtc_photo_t* src, int dst_w, int dst_h,  char* dst);
+void jrtc_nv12_rotate90(const struct jrtc_photo_t* src, int dst_w, int dst_h,  char* dst);
+void jrtc_uyvy_rotate90(const struct jrtc_photo_t* src, int dst_w, int dst_h,  char* dst);
+void jrtc_nv16_rotate90(const struct jrtc_photo_t* src, int dst_w, int dst_h,  char* dst);
+
+/** Ë³Ê±Õëת270¶È
+ * @param[in] dst_w Ä¿±ê¿í¶È, »á×Ô¶¯²Ã¼ô,µ«²»Ëõ·Å
+ * @param[in] dst_h Ä¿±ê¸ß¶È, ¸ºÊýʱ»áÏÈÉÏÏ·­×ª(µÈ¼ÛÓÚת270ºóˮƽ¾µÏñ)
+ * @param[out] dst  Ä¿±êÄÚ´æ
+ */
+void jrtc_i420_rotate270(const struct jrtc_photo_t* src, int dst_w, int dst_h,  char* dst);
+void jrtc_nv12_rotate270(const struct jrtc_photo_t* src, int dst_w, int dst_h,  char* dst);
+void jrtc_uyvy_rotate270(const struct jrtc_photo_t* src, int dst_w, int dst_h,  char* dst);
+void jrtc_nv16_rotate270(const struct jrtc_photo_t* src, int dst_w, int dst_h,  char* dst);
+
+/** ²Ã¼ô
+ * @param[in] dst_w Ä¿±ê¿í¶È, »á×Ô¶¯²Ã¼ô,µ«²»Ëõ·Å
+ * @param[in] dst_h Ä¿±ê¸ß¶È, ¸ºÊýʱ»áÏÈÉÏÏ·­×ª
+ * @param[out] dst  Ä¿±êÄÚ´æ
+ */
+void jrtc_i420_copy(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+void jrtc_nv12_copy(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+void jrtc_uyvy_copy(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+void jrtc_nv16_copy(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+
+/** ×ª»»Îª RGB565
+ * @param[in] dst_w Ä¿±ê¿í¶È, »á×Ô¶¯²Ã¼ô,µ«²»Ëõ·Å
+ * @param[in] dst_h Ä¿±ê¸ß¶È, ¸ºÊýʱ»áÏÈÉÏÏ·­×ª
+ * @param[out] dst  Ä¿±êÄÚ´æ, Êä³ö³¤¶ÈΪ dst_w*dst_h*sizeof(short)
+ */
+void jrtc_i420_torgb565(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+void jrtc_nv12_torgb565(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+void jrtc_uyvy_torgb565(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+void jrtc_nv16_torgb565(const struct jrtc_photo_t* src, int dst_w, int dst_h, char* dst);
+
+/** ¿ÉÓÅ»¯ÅäÖõIJÎÊý @{ */
+extern unsigned JRTC_SESSION_TIMEOUT; /*< ½¨Á¢Í¨»°×ܵij¬Ê±·§Öµ,ĬÈÏ 300 Ãë*/
+extern unsigned JRTC_REPLY_TIMEOUT; /*< ½¨Á¢Í¨»°Öеȴý·þÎñÆ÷Ó¦´ðµÄ³¬Ê±·§Öµ,ĬÈÏ120 Ãë */
+extern unsigned JRTC_MEDIA_TIMEOUT; /*< ½ÓÊÕýÌåÊý¾ÝµÄ³¬Ê±·§Öµ, Ä¬ÈÏ 20 Ãë */
+extern unsigned JRTC_MAX_RTP_SIZE; /*< ·¢Ë͵Ä×î´ó RTP °ü·§Öµ, Ä¬ÈÏ1200,²»ÄÜ´óÓÚ1500 */
+extern unsigned JRTC_AUDIO_RED_LOSS;/*< ¿ªÆôÒôƵÈßÓà±àÂëµÄ¶ª°üÂÊ·§Öµ, Ä¬ÈÏ 10 % */
+extern unsigned JRTC_VIDEO_FEC_LOSS; /*< ¿ªÆôÊÓÆµÈßÓà±àÂëµÄ¶ª°üÂÊ·§Öµ, Ä¬ÈÏ 5 % */
+extern unsigned JRTC_VIDEO_FEC_MAX_RTT;/*< ¿ªÆôÊÓÆµÈßÓà±àÂëµÄRTTÉÏÏÞ, Ä¬ÈÏ 3000 ms, 0 ÔòÇ¿ÖÆ¹Ø±ÕFEC */
+extern unsigned JRTC_VIDEO_FEC_MAX_BW_RATIO; /*< ¿ªÆôÊÓÆµÈßÓà±àÂëµÄ¶îÍâµÄÂëÂÊÕ¼±È, Ä¬ÈÏ 80 %, 0 ÔòÇ¿ÖÆ¹Ø±ÕFEC */
+extern unsigned JRTC_VIDEO_INIT_GOP; /*< ³õʼĬÈϵÄÊÓÆµ¹Ø¼üÖ¡¼ä¸ô, Ä¬ÈÏ2000 ms */
+extern unsigned JRTC_TASK_STACK_KB; /*< ÄÚ²¿Ï̵߳ÄÕ»´óС,ÓëÆ½Ì¨Ïà¹Ø */
+extern unsigned JRTC_TASK_PRIORITY; /*< ÄÚ²¿Ï̵߳ÄÓÅÏȼ¶,ÓëÆ½Ì¨Ïà¹Ø */
+/** @} */
+
+/** ¿É¸¨Öúµ÷ÊÔµÄÈ«¾ÖÖµ @{ */
+extern char  _jrtc_errstr[256]; /* ×î½ü´íÎóÃèÊö */
+extern const char* _jrtc_version; /* µ±Ç°°æ±¾ºÅ */
+/* ÒÔÏÂÊÇjrtc_config(...)ÖеÄÈë²ÎÖµ */
+extern char _jrtc_appkey[25];
+extern char _jrtc_uid[65];
+extern char _jrtc_srvURL[129];
+extern char _jrtc_aeskey[17];
+extern char _jrtc_tokens[129];
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
index 32af309..7268f18 100644 (file)
@@ -948,7 +948,7 @@ static void keypad_watch_cb(struct keypad_param *arg)
         }
     }
 
-       printf("[@CHENG_DEBUG][%s][%d]: key=%d,event=%d\n", __FUNCTION__, __LINE__, arg->data.key, arg->key_event);
+       //printf("[@CHENG_DEBUG][%s][%d]: key=%d,event=%d\n", __FUNCTION__, __LINE__, arg->data.key, arg->key_event);
 }
 
 lv_indev_t *keypad_dev;