rjw | 1f88458 | 2022-01-06 17:20:42 +0800 | [diff] [blame^] | 1 | #include "dtmf.h" |
| 2 | |
| 3 | #define USE_SYSLOG 1 |
| 4 | #if USE_SYSLOG |
| 5 | #include <log/log.h> |
| 6 | #endif |
| 7 | |
| 8 | #define RET_OK 0 |
| 9 | #define RET_FAIL -1 |
| 10 | |
| 11 | #define CRITICAL 0 |
| 12 | #define DEFAULT 1 |
| 13 | #define INFO 2 |
| 14 | #define DEBUGLEVEL DEFAULT |
| 15 | #define DTMF_TAG "[LIBDTMF]" |
| 16 | |
| 17 | static guint dtmf_get_time_ms(guint base) |
| 18 | { |
| 19 | guint current = g_get_monotonic_time() / G_TIME_SPAN_MILLISECOND; |
| 20 | if (current >= base) { |
| 21 | return (current - base); |
| 22 | } else { |
| 23 | return (G_MAXUINT32 - (base - current)); |
| 24 | } |
| 25 | } |
| 26 | #if USE_SYSLOG |
| 27 | #undef LOG_TAG |
| 28 | #define LOG_TAG "[LIBDTMF]" |
| 29 | #define PRINT_ERR(fmt, args...) RLOGE(fmt, ##args) |
| 30 | #define PRINT_DEFAULT(fmt, args...) RLOGI(fmt, ##args) |
| 31 | #define PRINT_INFO(fmt, args...) RLOGD(fmt, ##args) |
| 32 | #else |
| 33 | #define app_printf(level, fmt, args...) \ |
| 34 | do { if ((level) <= DEBUGLEVEL) \ |
| 35 | { fprintf (stderr, fmt, ##args); } } while (0) |
| 36 | |
| 37 | #define PRINT_ERR(fmt, args...) \ |
| 38 | app_printf(CRITICAL, DTMF_TAG"[ERR][L%d][T%d]"fmt, __LINE__, dtmf_get_time_ms(0),##args); |
| 39 | |
| 40 | #define PRINT_DEFAULT(fmt, args...) \ |
| 41 | app_printf(DEFAULT, DTMF_TAG"[DEF][L%d][T%d]"fmt, __LINE__, dtmf_get_time_ms(0), ##args); |
| 42 | |
| 43 | #define PRINT_INFO(fmt, args...) \ |
| 44 | app_printf(INFO, DTMF_TAG"[INFO][L%d][T%d]"fmt, __LINE__, dtmf_get_time_ms(0), ##args); |
| 45 | #endif |
| 46 | |
| 47 | #define FILE_NAME_MAX_LENGTH 64 |
| 48 | #define DTMFSRC_MUTE_LENGTH 100 //unit: ms, from the practice result, the begining 100ms data is 0. |
| 49 | |
| 50 | typedef struct { |
| 51 | DTMF_HANDLE handle; |
| 52 | gint number; //the event number: 0-15 |
| 53 | gint volume; //db value: 0-36 |
| 54 | gint time_ms; //the duration of tone, unit: ms |
| 55 | |
| 56 | pthread_t thread; |
| 57 | GMainLoop *loop; |
| 58 | GstElement *pipeline; |
| 59 | guint bus_watch_id; |
| 60 | GstState gst_cur_state; |
| 61 | |
| 62 | gint output; //0: output to pulsesink; 1: output the buffer pointer |
| 63 | char *path; //while output==1, file path to store the tone data |
| 64 | //FILE *fd; // file handle to the output file |
| 65 | } DTMF_PARAM_T; |
| 66 | |
| 67 | static int dtmf_stop_full(DTMF_PARAM_T *param) |
| 68 | { |
| 69 | GMainLoop *loop = param->loop; |
| 70 | GstElement *pipeline = param->pipeline; |
| 71 | guint bus_watch_id = param->bus_watch_id; |
| 72 | GstStructure *structure_stop = NULL; |
| 73 | GstEvent *event_stop = NULL; |
| 74 | |
| 75 | PRINT_DEFAULT("%s start, param: 0x%x, handle: 0x%x, number: %d, time_ms: %d, volume: %d \n", |
| 76 | __FUNCTION__, param, param->handle, param->number, param->time_ms, param->volume); |
| 77 | |
| 78 | if (param != param->handle) { |
| 79 | PRINT_DEFAULT("invalid handle: %p \n", param->handle); |
| 80 | return RET_OK; |
| 81 | } |
| 82 | |
| 83 | structure_stop = gst_structure_new ("dtmf-event", |
| 84 | "type", G_TYPE_INT, 1, |
| 85 | "number", G_TYPE_INT, 1, |
| 86 | "volume", G_TYPE_INT, 25, |
| 87 | "start", G_TYPE_BOOLEAN, FALSE, NULL); |
| 88 | event_stop = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, structure_stop); |
| 89 | gst_element_send_event (pipeline, event_stop); |
| 90 | |
| 91 | PRINT_DEFAULT("gst_element_set_state NULL start \n"); |
| 92 | gst_element_set_state (pipeline, GST_STATE_NULL); |
| 93 | PRINT_DEFAULT("gst_element_set_state NULL end \n"); |
| 94 | |
| 95 | gst_object_unref (GST_OBJECT (pipeline)); |
| 96 | g_source_remove (bus_watch_id); |
| 97 | g_main_loop_unref (loop); |
| 98 | |
| 99 | param->handle = NULL; |
| 100 | free(param); |
| 101 | param = NULL; |
| 102 | PRINT_DEFAULT("%s end \n", __FUNCTION__); |
| 103 | |
| 104 | return RET_OK; |
| 105 | } |
| 106 | |
| 107 | static gboolean dtmf_bus_call (GstBus *bus, GstMessage *msg, gpointer data) |
| 108 | { |
| 109 | DTMF_PARAM_T *param = (DTMF_PARAM_T *)data; |
| 110 | GstState oldstate, newstate, pending; |
| 111 | gchar *debug; |
| 112 | GError *error; |
| 113 | |
| 114 | switch (GST_MESSAGE_TYPE (msg)) { |
| 115 | case GST_MESSAGE_EOS: |
| 116 | PRINT_DEFAULT ("Receive GST_MESSAGE_EOS \n"); |
| 117 | g_main_loop_quit (param->loop); |
| 118 | break; |
| 119 | case GST_MESSAGE_STATE_CHANGED: |
| 120 | gst_message_parse_state_changed (msg, &oldstate, &newstate, &pending); |
| 121 | param->gst_cur_state = newstate; |
| 122 | PRINT_INFO ("GST_MESSAGE_STATE_CHANGED, oldstate[%d], newstate[%d], pending[%d] \n", |
| 123 | oldstate, newstate, pending); |
| 124 | break; |
| 125 | case GST_MESSAGE_ERROR: |
| 126 | gst_message_parse_error (msg, &error, &debug); |
| 127 | g_free (debug); |
| 128 | PRINT_ERR ("Error: %s\n", error->message); |
| 129 | g_error_free (error); |
| 130 | g_main_loop_quit (param->loop); |
| 131 | break; |
| 132 | default: |
| 133 | break; |
| 134 | } |
| 135 | |
| 136 | return TRUE; |
| 137 | } |
| 138 | |
| 139 | #if 0 |
| 140 | void dtmf_wait_ms(gint ms) |
| 141 | { |
| 142 | gint err; |
| 143 | struct timeval wait_tv; |
| 144 | wait_tv.tv_sec = ms / 1000; |
| 145 | wait_tv.tv_usec = (ms % 1000) * 1000; |
| 146 | do { |
| 147 | err = select(0, NULL, NULL, NULL, &wait_tv); |
| 148 | } while(err<0 && errno == EINTR); |
| 149 | } |
| 150 | |
| 151 | static GstFlowReturn dtmf_tone_data_output(GstElement *sink, void *data) |
| 152 | { |
| 153 | DTMF_PARAM_T *param = (DTMF_PARAM_T *)data; |
| 154 | GstSample *sample; |
| 155 | GstBuffer *buffer; |
| 156 | GstMapInfo map; |
| 157 | |
| 158 | sample = gst_app_sink_pull_sample (GST_APP_SINK (sink)); |
| 159 | if (!sample) |
| 160 | { |
| 161 | PRINT_ERR("could not get sample. \n"); |
| 162 | return GST_FLOW_ERROR; |
| 163 | } |
| 164 | buffer = gst_sample_get_buffer (sample); |
| 165 | gst_buffer_map (buffer, &map, GST_MAP_READ); |
| 166 | PRINT_INFO ("tone data: size=%d, data=%p \n", map.size, map.data); |
| 167 | fwrite(map.data, 1, map.size, param->fd); |
| 168 | |
| 169 | gst_buffer_unmap (buffer, &map); |
| 170 | gst_sample_unref (sample); |
| 171 | |
| 172 | return GST_FLOW_OK; |
| 173 | } |
| 174 | #endif |
| 175 | |
| 176 | static gint dtmf_check_param(DTMF_PARAM_T *param) |
| 177 | { |
| 178 | if ((param->number < 0) || (param->number > 15)) { |
| 179 | PRINT_ERR("invalid number: %d, valid value is [0,15] \n ", param->number); |
| 180 | return RET_FAIL; |
| 181 | } |
| 182 | if ((param->time_ms != 0) && (param->time_ms < DTMF_MIN_LENGTH)) { |
| 183 | PRINT_ERR("invalid time_ms: %d \n ", param->time_ms); |
| 184 | return RET_FAIL; |
| 185 | } |
| 186 | if ((param->volume < 0) || (param->volume > 36)) { |
| 187 | PRINT_ERR("invalid volume: %d, valid value is [0,36] \n ", param->volume); |
| 188 | return RET_FAIL; |
| 189 | } |
| 190 | if ((param->output < 0) || (param->output > 1)) { |
| 191 | PRINT_ERR("invalid output: %d, valid value is [0,1] \n ", param->output); |
| 192 | return RET_FAIL; |
| 193 | } |
| 194 | if ((param->output == 1) && (param->path == NULL)) { |
| 195 | PRINT_ERR("path is NULL while output is filesink \n "); |
| 196 | return RET_FAIL; |
| 197 | } |
| 198 | return RET_OK; |
| 199 | } |
| 200 | |
| 201 | void* dtmf_thread_func(void *arg) |
| 202 | { |
| 203 | DTMF_PARAM_T *param = (DTMF_PARAM_T *)arg; |
| 204 | PRINT_DEFAULT("%s start \n", __FUNCTION__); |
| 205 | |
| 206 | g_main_loop_run (param->loop); |
| 207 | PRINT_DEFAULT("g_main_loop_run end \n"); |
| 208 | |
| 209 | dtmf_stop_full(param); |
| 210 | |
| 211 | PRINT_DEFAULT("%s end \n", __FUNCTION__); |
| 212 | return ((void *)0); |
| 213 | } |
| 214 | |
| 215 | DTMF_HANDLE dtmf_start(gint num, gint time_ms, gint volume, DTMF_EXTRA *extra) |
| 216 | { |
| 217 | GMainLoop *loop; |
| 218 | GstElement *pipeline, *source, *sink; |
| 219 | GstBus *bus; |
| 220 | guint bus_watch_id; |
| 221 | pthread_t thread; |
| 222 | GstStructure *structure_start = NULL; |
| 223 | GstEvent *event_start = NULL; |
| 224 | |
| 225 | DTMF_HANDLE handle = NULL; |
| 226 | DTMF_PARAM_T *param = malloc(sizeof(DTMF_PARAM_T)); |
| 227 | if (param == NULL) { |
| 228 | PRINT_ERR("malloc DTMF_PARAM_T fail \n"); |
| 229 | } |
| 230 | memset(param, 0, sizeof(DTMF_PARAM_T)); |
| 231 | handle = (DTMF_HANDLE)param; |
| 232 | |
| 233 | //param->handle = handle; |
| 234 | param->number = num; |
| 235 | param->time_ms = time_ms; |
| 236 | param->volume = volume; |
| 237 | |
| 238 | PRINT_DEFAULT("%s start, handle: 0x%x, number: %d, time_ms: %d, volume: %d \n ", |
| 239 | __FUNCTION__, handle, param->number, param->time_ms, param->volume); |
| 240 | |
| 241 | if (extra != NULL) { |
| 242 | param->output = extra->output; |
| 243 | param->path = extra->path; |
| 244 | PRINT_DEFAULT("output: %d, path: %s \n", extra->output, extra->path); |
| 245 | } |
| 246 | |
| 247 | if (dtmf_check_param(param) < 0) { |
| 248 | PRINT_ERR("input parameter is invalid! \n"); |
| 249 | return NULL; |
| 250 | } |
| 251 | |
| 252 | gst_init (NULL, NULL); |
| 253 | |
| 254 | loop = g_main_loop_new (NULL, FALSE); |
| 255 | |
| 256 | pipeline = gst_pipeline_new ("dtmf-pipeline"); |
| 257 | source = gst_element_factory_make ("dtmfsrc", "dtmf-file-source"); |
| 258 | switch (param->output) { |
| 259 | case 0: |
| 260 | sink = gst_element_factory_make ("pulsesink", "dtmf-audio-output"); |
| 261 | break; |
| 262 | case 1: |
| 263 | sink = gst_element_factory_make ("filesink", "dtmf-audio-output"); |
| 264 | g_object_set(sink, "location", extra->path, NULL); |
| 265 | break; |
| 266 | default: |
| 267 | PRINT_ERR("input parameter 'output' is invalid! \n"); |
| 268 | break; |
| 269 | } |
| 270 | |
| 271 | //sink = gst_element_factory_make ("appsink", "audio-output-buffer"); |
| 272 | //g_object_set(sink, "emit-signals", TRUE, "sync", TRUE, NULL); |
| 273 | //g_signal_connect(sink, "new-sample", G_CALLBACK(tone_data_output), (gpointer)param); |
| 274 | |
| 275 | if (!pipeline || !source || !sink) { |
| 276 | PRINT_ERR ("One element creat fail, pipeline: %p, source: %p, sink: %p \n", |
| 277 | pipeline, source, sink); |
| 278 | free(param); |
| 279 | return NULL; |
| 280 | } |
| 281 | |
| 282 | guint interval = 50; |
| 283 | gint buffers_num = -1; |
| 284 | if (time_ms > 0) { |
| 285 | // dtmfsrc plugin will send EOS when the number of buffers reach 'buffers_num' . |
| 286 | g_object_get(source, "interval", &interval, NULL); |
| 287 | buffers_num = (time_ms + DTMFSRC_MUTE_LENGTH) / interval; |
| 288 | PRINT_DEFAULT("dtmfsrc interval: %d ms, set num-buffers: %d \n", |
| 289 | interval, buffers_num); |
| 290 | g_object_set(source, "num-buffers", buffers_num, NULL); |
| 291 | } |
| 292 | |
| 293 | bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); |
| 294 | bus_watch_id = gst_bus_add_watch (bus, dtmf_bus_call, (gpointer)param); |
| 295 | gst_object_unref (bus); |
| 296 | |
| 297 | param->pipeline = pipeline; |
| 298 | param->loop = loop; |
| 299 | param->bus_watch_id = bus_watch_id; |
| 300 | |
| 301 | pthread_create (&thread, NULL, (void *)dtmf_thread_func, param); |
| 302 | param->thread = thread; |
| 303 | |
| 304 | gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL); |
| 305 | gst_element_link_many (source, sink, NULL); |
| 306 | |
| 307 | gst_element_set_state (param->pipeline, GST_STATE_PLAYING); |
| 308 | |
| 309 | PRINT_INFO("wait GST_STATE_PLAYING start \n"); |
| 310 | do { |
| 311 | if (param->gst_cur_state == GST_STATE_PLAYING) |
| 312 | break; |
| 313 | else |
| 314 | usleep(5000); |
| 315 | } while (1); |
| 316 | PRINT_INFO("wait GST_STATE_PLAYING end \n"); |
| 317 | |
| 318 | structure_start = gst_structure_new ("dtmf-event", |
| 319 | "type", G_TYPE_INT, 1, |
| 320 | "number", G_TYPE_INT, param->number, |
| 321 | "volume", G_TYPE_INT, param->volume, |
| 322 | "start", G_TYPE_BOOLEAN, TRUE, NULL); |
| 323 | |
| 324 | //gst_structure_set (structure, "number", G_TYPE_INT, num, |
| 325 | // "volume", G_TYPE_INT, volume, NULL); |
| 326 | |
| 327 | event_start = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, structure_start); |
| 328 | gst_element_send_event (param->pipeline, event_start); |
| 329 | |
| 330 | PRINT_DEFAULT("%s end, handle: 0x%x, number: %d, time_ms: %d, volume: %d \n", |
| 331 | __FUNCTION__, handle, param->number, param->time_ms, param->volume); |
| 332 | |
| 333 | param->handle = handle; |
| 334 | |
| 335 | return handle; |
| 336 | } |
| 337 | |
| 338 | int dtmf_stop(DTMF_HANDLE handle) |
| 339 | { |
| 340 | if (handle == NULL) { |
| 341 | PRINT_ERR ("%s, handle is NULL\n", __FUNCTION__); |
| 342 | return RET_FAIL; |
| 343 | } |
| 344 | DTMF_PARAM_T *param = (DTMF_PARAM_T *)handle; |
| 345 | |
| 346 | PRINT_DEFAULT("%s start, DTMF_HANDLE: 0x%x, handle: 0x%x \n", |
| 347 | __FUNCTION__, handle, param->handle); |
| 348 | |
| 349 | if (handle != param->handle) { |
| 350 | PRINT_DEFAULT("invalid handle: %p \n", handle); |
| 351 | return RET_OK; |
| 352 | } |
| 353 | |
| 354 | g_main_loop_quit (param->loop); |
| 355 | pthread_join(param->thread, NULL); |
| 356 | |
| 357 | PRINT_DEFAULT("%s end, handle: 0x%x \n", __FUNCTION__, handle); |
| 358 | |
| 359 | return RET_OK; |
| 360 | } |