| /****************************************************************************** |
| *(C) Copyright 2019 ASR. |
| * All Rights Reserved |
| ******************************************************************************/ |
| /* ------------------------------------------------------------------------------------------------------------------- |
| * |
| * Filename:VoIP_service.c |
| * |
| * Authors: |
| * |
| * Description: The service will start the service of record and playback PCM stream based on ASR platforms. |
| * |
| * |
| * HISTORY: |
| * |
| * |
| * |
| * Notes: |
| * |
| ******************************************************************************/ |
| |
| /****************************************************************************** |
| * Include files |
| ******************************************************************************/ |
| #include "audio_if_types.h" |
| #include "audio_if_ubus.h" |
| #include "audio_if_parameter.h" |
| #include "audio_if.h" |
| #include "audio_if_api.h" |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include "telatparamdef_ubus.h" |
| #include <libubox/blobmsg_json.h> |
| #include "libubus.h" |
| #include <string.h> |
| #include "audio_if_audio_hw_mrvl.h" |
| #include "utlEventHandler.h" |
| #include "udev_monitor.h" |
| #include "audio_hw_mrvl.h" |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <cutils/str_parms.h> |
| #include "vcm.h" |
| #include <signal.h> |
| |
| extern void* audio_hal_install(void); |
| extern void audio_hal_uninstall(void); |
| extern void configure_vcm(unsigned int data[]); |
| extern int vcm_DTMFDetection(unsigned int onoff, unsigned int dialToneToOthersTones, unsigned int dialTonesToOthersDialTones, unsigned int dialVadDuration); |
| |
| /****************************************************************************** |
| * Globals |
| ******************************************************************************/ |
| static audio_hw_device_t *VoIP_ahw_dev_ubus; |
| |
| struct audio_stream_in *stream_in = NULL; |
| struct audio_stream_out *stream_out = NULL; |
| bool go_on_service = true; |
| unsigned int pcm_record_size = 0; //320:NB, 640:WB |
| unsigned int pcm_playback_size = 0; //320:NB, 640:WB |
| |
| |
| //#define IOCTL_DEV "/dev/audiostub_ctl" |
| //#define PCM_DEV "/dev/audiostub_pcm" |
| //#define VoIP_DEV "/dev/VoIP_ctl" |
| |
| //add the device interface for test |
| //#define TEST_VoIP_DEV "/dev/VoIP_pcm" |
| |
| #define OPEN_VoIP_DEV 1 |
| //#define OPEN_TEST_VoIP_DEV 2 |
| |
| #define CONFIG_RECORD_FMT 1 |
| #define CONFIG_PLAYBACK_FMT 0 |
| ///////////////////////////////////////////////////////////////////////////// |
| |
| static int config_parameters(int in_out, int is_wb, int near_far_end, int near_codec_or_vocoder) |
| { |
| unsigned int direction = 0xFF, type, srcdst, priority, dest; |
| char kvpair[128]; |
| struct str_parms *param = NULL; |
| int data[5]; |
| const char *key = NULL; |
| bool update_vcm = false; |
| |
| direction = in_out;/* 0-play, 1-record */ |
| type = is_wb; /* 0:PCM_NB_BUF_SIZE, 1:PCM_WB_BUF_SIZE */ |
| srcdst = near_far_end;/* 0-None, 1-Near end, 2-Far end, 3-Both ends */ |
| priority = 1;/* 0-Do not combine(override), 1-Combine */ |
| dest = near_codec_or_vocoder;/* 0-Near codec, 1-Near Vocoder */ |
| |
| if(direction == 0){//output |
| if(type == 0) |
| pcm_playback_size = PCM_NB_BUF_SIZE; |
| else |
| pcm_playback_size = PCM_WB_BUF_SIZE; |
| |
| printf("config playback parameters.\n"); |
| } |
| else if(direction == 1){//input |
| if(type == 0) |
| pcm_record_size = PCM_NB_BUF_SIZE; |
| else |
| pcm_record_size = PCM_WB_BUF_SIZE; |
| |
| printf("config record parameters.\n"); |
| } |
| |
| memset(kvpair, 0x00, sizeof(kvpair)); |
| sprintf(kvpair, "%s=%d;%s=%d;%s=%d;%s=%d;%s=%d", VCM_CONFIG_DIRECTION, direction, |
| VCM_CONFIG_TYPE, type, VCM_CONFIG_SRC_DST, srcdst, |
| VCM_CONFIG_PRIORITY, priority, VCM_CONFIG_DEST, dest); |
| |
| printf("%s: config information kvpair is %s.\n", __FUNCTION__, kvpair); |
| |
| //extract the parameter and config from string |
| param = str_parms_create_str(kvpair); |
| if (!param) { |
| printf("%s: param create str is null!", __FUNCTION__); |
| return -1; |
| } |
| |
| //set vcm configurations |
| key = VCM_CONFIG_DIRECTION; |
| if (str_parms_get_int(param, key, &data[0]) == 0) { |
| update_vcm = true; |
| str_parms_del(param, key); |
| } |
| key = VCM_CONFIG_TYPE; |
| if (str_parms_get_int(param, key, &data[1]) == 0) { |
| update_vcm = true; |
| str_parms_del(param, key); |
| } |
| key = VCM_CONFIG_SRC_DST; |
| if (str_parms_get_int(param, key, &data[2]) == 0) { |
| update_vcm = true; |
| str_parms_del(param, key); |
| } |
| key = VCM_CONFIG_PRIORITY; |
| if (str_parms_get_int(param, key, &data[3]) == 0) { |
| update_vcm = true; |
| str_parms_del(param, key); |
| } |
| key = VCM_CONFIG_DEST; |
| if (str_parms_get_int(param, key, &data[4]) == 0) { |
| update_vcm = true; |
| str_parms_del(param, key); |
| } |
| |
| //printf("Direction is %d, Type is %d, Src_Dst is %d, Priority is %d, Dest is %d. \n",data[0], data[1], data[2], data[3], data[4]); |
| |
| if (update_vcm) { |
| configure_vcm(data); /*TODO check if all inputs got all values successfully*/ |
| } |
| |
| return 0; |
| } |
| |
| /*******************************************************************************\ |
| * Function: VoIP_thread |
| * Description:This function will be main thread to start the service of record and |
| * and playback. |
| * |
| * NOTE: |
| sample rate is 8000 |
| channel number is 1/MONO |
| bit of sample is 16 |
| Due to modem limited, only support 8k/16k sample rate |
| * Returns: void |
| \*******************************************************************************/ |
| static void VoIP_thread(void *arg) |
| { |
| int rc, len; |
| char buffer[PCM_WB_BUF_SIZE]; |
| |
| printf("enter VoIP_thread.\n"); |
| |
| //Must set vcm_configure before playback |
| if((pcm_record_size != PCM_NB_BUF_SIZE) && (pcm_record_size != PCM_WB_BUF_SIZE)){ |
| printf("%s: Please use vcm_configure to set pcm_record_size!!\n", __FUNCTION__); |
| return; |
| } |
| |
| //Must set vcm_configure before playback |
| if((pcm_playback_size != PCM_NB_BUF_SIZE) && (pcm_playback_size != PCM_WB_BUF_SIZE)){ |
| printf("%s: Please use vcm_configure to set pcm_playback_size!!\n", __FUNCTION__); |
| return; |
| } |
| |
| //Must set vcm_configure before playback |
| if(pcm_record_size != pcm_playback_size){ |
| printf("%s: Please configure pcm_record_size = pcm_playback_size!!\n", __FUNCTION__); |
| return; |
| } |
| |
| //open the audiostub_ctl, prepare for record and playback |
| VCMInit(); |
| |
| //open record stream |
| rc = VoIP_ahw_dev_ubus->open_input_stream(VoIP_ahw_dev_ubus, 0, |
| VoIP_ahw_dev_ubus->get_supported_devices(VoIP_ahw_dev_ubus), |
| NULL, &stream_in, 0, 0, AUDIO_SOURCE_VOICE_CALL); |
| if (rc < 0) { |
| printf("%s: error opening input device. rc = %d!\n", __FUNCTION__, rc); |
| goto bad_stream; |
| } |
| |
| //open playback stream |
| rc = VoIP_ahw_dev_ubus->open_output_stream(VoIP_ahw_dev_ubus, 0, |
| VoIP_ahw_dev_ubus->get_supported_devices(VoIP_ahw_dev_ubus), |
| AUDIO_OUTPUT_FLAG_DIRECT, NULL, &stream_out, 0); |
| if (rc < 0) { |
| printf("%s: error opening output device. rc = %d!\n", __FUNCTION__, rc); |
| goto bad_stream; |
| } |
| |
| //bypass VoIP Record, only send the command "AUDIOSTUB_PCMRECCTL" |
| stream_in->read(stream_in, buffer, pcm_record_size); |
| |
| //bypass VoIP playback, only send the command "AUDIOSTUB_PCMPLAYBACKCTL" |
| stream_out->write(stream_out, buffer, pcm_record_size); |
| |
| printf("%s: waiting for ending of pcm stream service!\n", __FUNCTION__); |
| |
| go_on_service = true; |
| while (go_on_service) { |
| |
| //wait the program ending. |
| sleep(1); |
| |
| } |
| |
| //close the VoIP device FD when quit the programe. |
| stream_in->common.standby(&stream_in->common); |
| VoIP_ahw_dev_ubus->close_input_stream(VoIP_ahw_dev_ubus, stream_in); |
| stream_out->common.standby(&stream_out->common); |
| VoIP_ahw_dev_ubus->close_output_stream(VoIP_ahw_dev_ubus, stream_out); |
| VCMDeinit();//close the fd of audiostub_ctl when exit the thread. |
| |
| go_on_service = false; |
| |
| bad_stream: |
| printf("%s: finished pcm stream service!\n", __FUNCTION__); |
| printf("exit VoIP_thread!\n"); |
| return; |
| } |
| |
| /*******************************************************************************\ |
| * Function: simulateOffhook |
| * send the command of "AUDIOSTUB_PCMCTL" and "AUDIOSTUB_DTMFDETECTIONCTL" to simulate telephone offhook. |
| \*******************************************************************************/ |
| static void simulateOffhook(unsigned int onoff) |
| { |
| unsigned int pcm_on; |
| unsigned int DTMFDetectiononoff; |
| unsigned int dialToneToOthersTones; |
| unsigned int dialTonesToOthersDialTones; |
| unsigned int dialVadDuration; |
| |
| pcm_on = onoff; |
| //send the command of "AUDIOSTUB_PCMCTL" |
| VoIP_ahw_dev_ubus->switch_pcm(VoIP_ahw_dev_ubus, pcm_on); |
| |
| DTMFDetectiononoff = onoff; |
| dialToneToOthersTones = 50; |
| dialTonesToOthersDialTones = 4; |
| dialVadDuration = 3; |
| //send the command of "AUDIOSTUB_DTMFDETECTIONCTL" |
| //vcm_DTMFDetection(1, 50, 4, 3); |
| vcm_DTMFDetection(DTMFDetectiononoff, dialToneToOthersTones, dialTonesToOthersDialTones, dialVadDuration); |
| |
| return; |
| } |
| |
| /*******************************************************************************\ |
| * Function: sig_handler |
| * handle the signal of "CTRL+C" and "kill -15" to end the VoIP service |
| \*******************************************************************************/ |
| static void sig_handler(int sig) |
| { |
| //simulate telephone onhook |
| simulateOffhook(0); |
| |
| go_on_service = false; |
| printf("output signal number: %d.\n", sig); |
| printf("ending the service of record and playback!\n"); |
| |
| return; |
| } |
| |
| /*******************************************************************************\ |
| * Function: show_usage |
| \*******************************************************************************/ |
| static void show_usage(void) |
| { |
| printf("ASR VoIP_service\n"); |
| printf("Usage: VoIP_service [sampling_rate] scenario\n"); |
| printf(" \n"); |
| printf(" sampling_rate nb set PCM sampling rate to narrowband 8kHz\n"); |
| printf(" wb set PCM sampling rate to wideband 16kHz\n"); |
| printf(" default value is nb\n"); |
| printf(" scenario 1 start service for scenario 1\n"); |
| printf(" 2 start service for scenario 2\n"); |
| } |
| |
| /*******************************************************************************\ |
| * Function: main |
| \*******************************************************************************/ |
| int main(int argc, char**argv) |
| { |
| pthread_t thread_start; |
| int rc = 0; |
| int i = 0; |
| unsigned int is_wb = 2; /* 0:PCM_NB_BUF_SIZE, 1:PCM_WB_BUF_SIZE */ |
| unsigned int near_far_end = 4; /* 0-None, 1-Near end, 2-Far end, 3-Both ends */ |
| unsigned int near_codec_or_vocoder = 2; /* 0-Near codec, 1-Near Vocoder */ |
| |
| if(argc == 2) |
| { |
| is_wb = 0; |
| if(strcmp(argv[1], "1") == 0) |
| { |
| near_far_end = 1; |
| near_codec_or_vocoder = 1; |
| } |
| else if(strcmp(argv[1], "2") == 0) |
| { |
| near_far_end = 2; |
| near_codec_or_vocoder = 0; |
| } |
| else |
| { |
| show_usage(); |
| exit (-1); |
| } |
| } |
| else if(argc == 3) |
| { |
| for (i=1; i<argc; i++) |
| {
|
| if(strcmp(argv[i], "nb") == 0) |
| { |
| is_wb = 0; |
| } |
| else if(strcmp(argv[i], "wb") == 0) |
| { |
| is_wb = 1; |
| } |
| else if(strcmp(argv[i], "1") == 0) |
| { |
| near_far_end = 1; |
| near_codec_or_vocoder = 1; |
| } |
| else if(strcmp(argv[i], "2") == 0) |
| { |
| near_far_end = 2; |
| near_codec_or_vocoder = 0; |
| } |
| else |
| { |
| show_usage(); |
| exit (-1); |
| } |
| } |
| } |
| else |
| { |
| show_usage(); |
| exit (-1); |
| } |
| |
| if(is_wb<2 && near_far_end<4 && near_codec_or_vocoder<2) |
| { |
| printf("start VoIP service with %s PCM sampling rate for scenario %d: %s\n", 0==is_wb?"nb":"wb", near_far_end, 1==near_far_end?"Near end | Near vocoder":"Far end | Near codec"); |
| } |
| else |
| { |
| show_usage(); |
| exit (-1); |
| } |
| |
| //use the VoIP device file. It means starting service. |
| config_VoIP_device(OPEN_VoIP_DEV); |
| |
| /* install signal handler and begin to capture signal for close */ |
| signal(SIGINT, sig_handler); |
| signal(SIGTERM, sig_handler); |
| |
| //init global variables |
| VoIP_ahw_dev_ubus = audio_hal_install(); |
| if (VoIP_ahw_dev_ubus == NULL) { |
| printf("%s: audio_hal_install failed!\n", __FUNCTION__); |
| exit (-1); |
| } |
| |
| //send the command of "AUDIOSTUB_PCMCTL" and "AUDIOSTUB_DTMFDETECTIONCTL" to simulate telephone offhook. |
| simulateOffhook(1); |
| |
| //The following config parameters are needed for main thread. |
| //config record parameters. |
| config_parameters(CONFIG_RECORD_FMT, is_wb, near_far_end, near_codec_or_vocoder); |
| //config playback parameters. |
| config_parameters(CONFIG_PLAYBACK_FMT, is_wb, near_far_end, near_codec_or_vocoder); |
| |
| rc = pthread_create(&thread_start, NULL, (void *)&VoIP_thread, NULL); |
| if (rc < 0) { |
| printf("%s: error creating thread_start!\n", __FUNCTION__); |
| rc = UBUS_STATUS_PERMISSION_DENIED; |
| } |
| |
| if (pthread_join(thread_start, NULL)){ |
| printf("error join thread!\n"); |
| abort(); |
| } |
| |
| audio_hal_uninstall(); |
| |
| return 0; |
| } |
| |