diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index c6af072cf..90f152661 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -48,6 +48,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "mediastreamer2/msjpegwriter.h" #include "mediastreamer2/msogl.h" #include "mediastreamer2/msvolume.h" +#include "mediastreamer2/msqrcodereader.h" #include "bctoolbox/charconv.h" #include "chat/chat-room/client-group-chat-room-p.h" @@ -447,6 +448,14 @@ void linphone_core_cbs_set_chat_room_state_changed (LinphoneCoreCbs *cbs, Linpho cbs->vtable->chat_room_state_changed = cb; } +LinphoneCoreCbsQrcodeFoundedCb linphone_core_cbs_get_qrcode_founded(LinphoneCoreCbs *cbs) { + return cbs->vtable->qrcode_founded; +} + +void linphone_core_cbs_set_qrcode_founded(LinphoneCoreCbs *cbs, LinphoneCoreCbsQrcodeFoundedCb cb) { + cbs->vtable->qrcode_founded = cb; +} + void linphone_core_cbs_set_ec_calibration_result(LinphoneCoreCbs *cbs, LinphoneCoreCbsEcCalibrationResultCb cb) { cbs->vtable->ec_calibration_result = cb; } @@ -3129,15 +3138,15 @@ static void monitor_network_state(LinphoneCore *lc, time_t curtime){ bool_t new_status=lc->network_last_status; char newip[LINPHONE_IPADDR_SIZE]; - /* only do the network up checking every five seconds */ + // only do the network up checking every five seconds if (lc->network_last_check==0 || (curtime-lc->network_last_check)>=5){ linphone_core_get_local_ip(lc,AF_UNSPEC,NULL,newip); if (strcmp(newip,"::1")!=0 && strcmp(newip,"127.0.0.1")!=0){ new_status=TRUE; - }else new_status=FALSE; /*no network*/ + }else new_status=FALSE; //no network if (new_status==lc->network_last_status && new_status==TRUE && strcmp(newip,lc->localip)!=0){ - /*IP address change detected*/ + //IP address change detected ms_message("IP address change detected."); set_network_reachable(lc,FALSE,curtime); lc->network_last_status=FALSE; @@ -4791,11 +4800,22 @@ void linphone_core_migrate_logs_from_rc_to_db(LinphoneCore *lc) { * Video related functions * ******************************************************************************/ + #ifdef VIDEO_ENABLED -static void snapshot_taken(void *userdata, struct _MSFilter *f, unsigned int id, void *arg) { - if (id == MS_JPEG_WRITER_SNAPSHOT_TAKEN) { - LinphoneCore *lc = (LinphoneCore *)userdata; - linphone_core_enable_video_preview(lc, FALSE); +static void video_filter_callback(void *userdata, struct _MSFilter *f, unsigned int id, void *arg) { + switch(id) { + case MS_JPEG_WRITER_SNAPSHOT_TAKEN: { + LinphoneCore *lc = (LinphoneCore *)userdata; + linphone_core_enable_video_preview(lc, FALSE); + break; + } + case MS_QRCODE_READER_QRCODE_FOUND: { + LinphoneCore *lc = (LinphoneCore *)userdata; + if (linphone_core_cbs_get_qrcode_founded(linphone_core_get_current_callbacks(lc)) != NULL) { + linphone_core_notify_qrcode_founded(lc, (const char*)arg); + } + break; + } } } #endif @@ -4818,7 +4838,7 @@ LinphoneStatus linphone_core_take_preview_snapshot(LinphoneCore *lc, const char lc->previewstream->ms.factory = lc->factory; linphone_core_enable_video_preview(lc, TRUE); - ms_filter_add_notify_callback(lc->previewstream->local_jpegwriter, snapshot_taken, lc, TRUE); + ms_filter_add_notify_callback(lc->previewstream->local_jpegwriter, video_filter_callback, lc, TRUE); ms_filter_call_method(lc->previewstream->local_jpegwriter, MS_JPEG_WRITER_TAKE_SNAPSHOT, (void*)file); } else { ms_filter_call_method(lc->previewstream->local_jpegwriter, MS_JPEG_WRITER_TAKE_SNAPSHOT, (void*)file); @@ -4848,7 +4868,11 @@ static void toggle_video_preview(LinphoneCore *lc, bool_t val){ if (lc->preview_window_id != NULL) video_preview_set_native_window_id(lc->previewstream,lc->preview_window_id); video_preview_set_fps(lc->previewstream,linphone_core_get_preferred_framerate(lc)); + if (linphone_core_qrcode_video_preview_enabled(lc)) + video_preview_enable_qrcode(lc->previewstream, TRUE); video_preview_start(lc->previewstream,lc->video_conf.device); + if (video_preview_qrcode_enabled(lc->previewstream)) + ms_filter_add_notify_callback(lc->previewstream->qrcode, video_filter_callback, lc, TRUE); } }else{ if (lc->previewstream!=NULL){ @@ -5023,6 +5047,16 @@ bool_t linphone_core_video_preview_enabled(const LinphoneCore *lc){ return lc->video_conf.show_local; } +void linphone_core_enable_qrcode_video_preview(LinphoneCore *lc, bool_t val) { + lc->video_conf.qrcode_decoder=val; + if (linphone_core_ready(lc)) + lp_config_set_int(lc->config,"video","qrcode_decoder",val); +} + +bool_t linphone_core_qrcode_video_preview_enabled(const LinphoneCore *lc) { + return lc->video_conf.qrcode_decoder; +} + void linphone_core_enable_self_view(LinphoneCore *lc, bool_t val){ #ifdef VIDEO_ENABLED LinphoneCall *call=linphone_core_get_current_call (lc); diff --git a/coreapi/private_functions.h b/coreapi/private_functions.h index e6a27fbe0..2e8c43984 100644 --- a/coreapi/private_functions.h +++ b/coreapi/private_functions.h @@ -510,7 +510,7 @@ void linphone_core_notify_friend_list_removed(LinphoneCore *lc, LinphoneFriendLi void linphone_core_notify_call_created(LinphoneCore *lc, LinphoneCall *call); void linphone_core_notify_version_update_check_result_received(LinphoneCore *lc, LinphoneVersionUpdateCheckResult result, const char *version, const char *url); void linphone_core_notify_chat_room_state_changed (LinphoneCore *lc, LinphoneChatRoom *cr, LinphoneChatRoomState state); - +void linphone_core_notify_qrcode_founded(LinphoneCore *lc, const char *result); void linphone_core_notify_ec_calibration_result(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms); void linphone_core_notify_ec_calibration_audio_init(LinphoneCore *lc); void linphone_core_notify_ec_calibration_audio_uninit(LinphoneCore *lc); diff --git a/coreapi/private_structs.h b/coreapi/private_structs.h index 3bcfc4d78..3c1016a9e 100644 --- a/coreapi/private_structs.h +++ b/coreapi/private_structs.h @@ -326,6 +326,7 @@ struct video_config{ float fps; bool_t capture; bool_t show_local; + bool_t qrcode_decoder; bool_t display; bool_t selfview; /*during calls*/ bool_t reuse_preview_source; diff --git a/coreapi/vtables.c b/coreapi/vtables.c index 13472609c..7df79ee01 100644 --- a/coreapi/vtables.c +++ b/coreapi/vtables.c @@ -298,6 +298,11 @@ void linphone_core_notify_chat_room_state_changed (LinphoneCore *lc, LinphoneCha cleanup_dead_vtable_refs(lc); } +void linphone_core_notify_qrcode_founded(LinphoneCore *lc, const char *result) { + NOTIFY_IF_EXIST(qrcode_founded, lc, result); + cleanup_dead_vtable_refs(lc); +} + void linphone_core_notify_ec_calibration_result(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms) { NOTIFY_IF_EXIST(ec_calibration_result, lc, status, delay_ms); cleanup_dead_vtable_refs(lc); diff --git a/include/linphone/callbacks.h b/include/linphone/callbacks.h index 7511a1d97..4e465c2d2 100644 --- a/include/linphone/callbacks.h +++ b/include/linphone/callbacks.h @@ -405,6 +405,13 @@ typedef void (*LinphoneCoreCbsVersionUpdateCheckResultReceivedCb) (LinphoneCore */ typedef void (*LinphoneCoreCbsChatRoomStateChangedCb) (LinphoneCore *lc, LinphoneChatRoom *cr, LinphoneChatRoomState state); +/** + * Callback prototype telling the result of decoded qrcode + * @param[in] lc LinphoneCore object + * @param[in] result The result of the decoded qrcode + */ +typedef void (*LinphoneCoreCbsQrcodeFoundedCb)(LinphoneCore *lc, const char *result); + /** * @} **/ diff --git a/include/linphone/core.h b/include/linphone/core.h index 7ee26c5d0..eebfc3b62 100644 --- a/include/linphone/core.h +++ b/include/linphone/core.h @@ -194,6 +194,7 @@ typedef struct _LinphoneCoreVTable{ LinphoneCoreCbsCallCreatedCb call_created; LinphoneCoreCbsVersionUpdateCheckResultReceivedCb version_update_check_result_received; LinphoneCoreCbsChatRoomStateChangedCb chat_room_state_changed; + LinphoneCoreCbsQrcodeFoundedCb qrcode_founded; LinphoneCoreCbsEcCalibrationResultCb ec_calibration_result; LinphoneCoreCbsEcCalibrationAudioInitCb ec_calibration_audio_init; LinphoneCoreCbsEcCalibrationAudioUninitCb ec_calibration_audio_uninit; @@ -687,6 +688,20 @@ LINPHONE_PUBLIC LinphoneCoreCbsChatRoomStateChangedCb linphone_core_cbs_get_chat */ LINPHONE_PUBLIC void linphone_core_cbs_set_chat_room_state_changed (LinphoneCoreCbs *cbs, LinphoneCoreCbsChatRoomStateChangedCb cb); +/** + * Get the qrcode founded callback. + * @param[in] cbs LinphoneCoreCbs object + * @return The current callback + */ +LINPHONE_PUBLIC LinphoneCoreCbsQrcodeFoundedCb linphone_core_cbs_get_qrcode_founded(LinphoneCoreCbs *cbs); + +/** + * Set the qrcode found callback. + * @param[in] cbs LinphoneCoreCbs object + * @param[in] cb The callback to use + **/ +LINPHONE_PUBLIC void linphone_core_cbs_set_qrcode_founded(LinphoneCoreCbs *cbs, LinphoneCoreCbsQrcodeFoundedCb cb); + /** * @brief Sets a callback to call each time the echo-canceler calibration is completed. */ @@ -3606,6 +3621,22 @@ LINPHONE_PUBLIC void linphone_core_enable_video_preview(LinphoneCore *lc, bool_t **/ LINPHONE_PUBLIC bool_t linphone_core_video_preview_enabled(const LinphoneCore *lc); +/** + * Controls QRCode enablement. + * @param[in] lc LinphoneCore object + * @param[in] val A boolean value telling whether the QRCode is enabled in the preview. + * @ingroup media_parameters + **/ +LINPHONE_PUBLIC void linphone_core_enable_qrcode_video_preview(LinphoneCore *lc, bool_t val); + +/** + * Tells whether QRCode is enabled in the preview. + * @param[in] lc LinphoneCore object + * @return A boolean value telling whether QRCode is enabled in the preview. + * @ingroup media_parameters + **/ +LINPHONE_PUBLIC bool_t linphone_core_qrcode_video_preview_enabled(const LinphoneCore *lc); + /** * Take a photo of currently from capture device and write it into a jpeg file. * Note that the snapshot is asynchronous, an application shall not assume that the file is created when the function returns. diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt index b14e28572..0a1db7740 100644 --- a/tester/CMakeLists.txt +++ b/tester/CMakeLists.txt @@ -139,6 +139,7 @@ set(RC_FILES set(IMAGE_FILES images/linphone.svg + images/linphonesiteqr.jpg images/nowebcamCIF.jpg images/nowebcamVGA.jpg ) diff --git a/tester/images/linphonesiteqr.jpg b/tester/images/linphonesiteqr.jpg new file mode 100644 index 000000000..21768642c Binary files /dev/null and b/tester/images/linphonesiteqr.jpg differ diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 9824c2084..6c83d1c8a 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -128,6 +128,7 @@ extern bool_t liblinphone_tester_keep_uuid; extern bool_t liblinphone_tester_tls_support_disabled; extern const MSAudioDiffParams audio_cmp_params; extern const char *liblinphone_tester_mire_id; +extern const char *liblinphone_tester_static_image_id; extern bool_t liblinphonetester_ipv6; extern bool_t liblinphonetester_show_account_manager_logs; diff --git a/tester/tester.c b/tester/tester.c index c47cff87f..f6d4069e7 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -51,6 +51,7 @@ int liblinphonetester_transport_timeout = 9000; /*milliseconds. it is set to suc Thanks to the timeout, it will fallback to IPv4*/ const char *liblinphone_tester_mire_id="Mire: Mire (synthetic moving picture)"; +const char *liblinphone_tester_static_image_id="StaticImage: Static picture"; static void network_reachable(LinphoneCore *lc, bool_t reachable) { stats* counters; diff --git a/tester/video_tester.c b/tester/video_tester.c index d44b0ee0f..da1ac6dec 100644 --- a/tester/video_tester.c +++ b/tester/video_tester.c @@ -23,6 +23,8 @@ #include "tester_utils.h" #include "linphone/lpconfig.h" +#include + static void enable_disable_camera_after_camera_switches(void) { LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); @@ -61,8 +63,60 @@ static void enable_disable_camera_after_camera_switches(void) { linphone_core_manager_destroy(pauline); } +typedef struct struct_qrcode_callback_data { + int qrcode_found; + char *text; +}qrcode_callback_data; + + +static void qrcode_found_cb(LinphoneCore *lc, const char *result) { + LinphoneCoreCbs *cbs = linphone_core_get_current_callbacks(lc); + qrcode_callback_data *found = (qrcode_callback_data *)linphone_core_cbs_get_user_data(cbs); + found->qrcode_found = TRUE; + if (result) { + if (found->text) ms_free(found->text); + found->text = ms_strdup(result); + } +} + +static void decode_qrcode_from_nowebcam(void) { + qrcode_callback_data qrcode_data; + char *qrcode_image; + LinphoneCoreManager* lcm = linphone_core_manager_create("empty_rc"); + LinphoneCoreCbs* cbs = NULL; + qrcode_data.qrcode_found = FALSE; + qrcode_data.text = NULL; + linphone_core_manager_start(lcm, FALSE); + + qrcode_image = bc_tester_res("images/linphonesiteqr.jpg"); + + linphone_core_set_video_device(lcm->lc, liblinphone_tester_static_image_id); + linphone_core_set_static_picture(lcm->lc, qrcode_image); + + linphone_core_enable_qrcode_video_preview(lcm->lc, TRUE); + cbs = linphone_core_get_current_callbacks(lcm->lc); + linphone_core_cbs_set_qrcode_founded(cbs, qrcode_found_cb); + linphone_core_cbs_set_user_data(cbs, &qrcode_data); + linphone_core_enable_video_preview(lcm->lc, TRUE); + + BC_ASSERT_TRUE(wait_for_until(lcm->lc, NULL, &qrcode_data.qrcode_found, TRUE, 3000)); + if (qrcode_data.qrcode_found) { + if (BC_ASSERT_PTR_NOT_NULL(qrcode_data.text)) { + ms_message("QRCode decode: %s", qrcode_data.text); + BC_ASSERT_STRING_EQUAL(qrcode_data.text, "https://www.linphone.org/"); + } + } + + if (qrcode_data.text) ms_free(qrcode_data.text); + if (qrcode_image) ms_free(qrcode_image); + + linphone_core_enable_video_preview(lcm->lc, FALSE); + linphone_core_manager_destroy(lcm); +} + test_t video_tests[] = { - TEST_NO_TAG("Enable/disable camera after camera switches", enable_disable_camera_after_camera_switches) + TEST_NO_TAG("Enable/disable camera after camera switches", enable_disable_camera_after_camera_switches), + TEST_NO_TAG("Decode QRCode", decode_qrcode_from_nowebcam) }; test_suite_t video_test_suite = {