blob: 936dd0f4e45e072df2fbc768051bb6fd1e3814ad [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001#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
17static 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
50typedef 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
67static 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
107static 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
140void 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
151static 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
176static 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
201void* 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
215DTMF_HANDLE dtmf_start(gint num, gint time_ms, gint volume, DTMF_EXTRA *extra)
216{
217 GMainLoop *loop;
218 GstElement *pipeline = NULL, *source = NULL, *sink = NULL;
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 free(param);
249 PRINT_ERR("input parameter is invalid! \n");
250 return NULL;
251 }
252
253 gst_init (NULL, NULL);
254
255 loop = g_main_loop_new (NULL, FALSE);
256
257 pipeline = gst_pipeline_new ("dtmf-pipeline");
258 source = gst_element_factory_make ("dtmfsrc", "dtmf-file-source");
259 switch (param->output) {
260 case 0:
261 sink = gst_element_factory_make ("pulsesink", "dtmf-audio-output");
262 break;
263 case 1:
264 sink = gst_element_factory_make ("filesink", "dtmf-audio-output");
265 g_object_set(sink, "location", extra->path, NULL);
266 break;
267 default:
268 PRINT_ERR("input parameter 'output' is invalid! \n");
269 break;
270 }
271
272 //sink = gst_element_factory_make ("appsink", "audio-output-buffer");
273 //g_object_set(sink, "emit-signals", TRUE, "sync", TRUE, NULL);
274 //g_signal_connect(sink, "new-sample", G_CALLBACK(tone_data_output), (gpointer)param);
275
276 if (!pipeline || !source || !sink) {
277 PRINT_ERR ("One element creat fail, pipeline \n");
278 free(param);
279 if (pipeline != NULL) {
280 PRINT_ERR ("pipeline is not null\n");
281 gst_object_unref(GST_OBJECT(pipeline));
282 }
283 if (source != NULL) {
284 PRINT_ERR ("source is not null\n");
285 gst_object_unref(GST_OBJECT(source));
286 }
287 if (sink != NULL) {
288 PRINT_ERR ("sink is not null\n");
289 gst_object_unref(GST_OBJECT(sink));
290 }
291 g_main_loop_unref (loop);
292 return NULL;
293 }
294
295 guint interval = 50;
296 gint buffers_num = -1;
297 if (time_ms > 0) {
298 // dtmfsrc plugin will send EOS when the number of buffers reach 'buffers_num' .
299 g_object_get(source, "interval", &interval, NULL);
300 buffers_num = (time_ms + DTMFSRC_MUTE_LENGTH) / interval;
301 PRINT_DEFAULT("dtmfsrc interval: %d ms, set num-buffers: %d \n",
302 interval, buffers_num);
303 g_object_set(source, "num-buffers", buffers_num, NULL);
304 }
305
306 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
307 bus_watch_id = gst_bus_add_watch (bus, dtmf_bus_call, (gpointer)param);
308 gst_object_unref (bus);
309
310 param->pipeline = pipeline;
311 param->loop = loop;
312 param->bus_watch_id = bus_watch_id;
313
314 pthread_create (&thread, NULL, (void *)dtmf_thread_func, param);
315 param->thread = thread;
316
317 gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
318 gst_element_link_many (source, sink, NULL);
319
320 gst_element_set_state (param->pipeline, GST_STATE_PLAYING);
321
322 PRINT_INFO("wait GST_STATE_PLAYING start \n");
323 do {
324 if (param->gst_cur_state == GST_STATE_PLAYING)
325 break;
326 else
327 usleep(5000);
328 } while (1);
329 PRINT_INFO("wait GST_STATE_PLAYING end \n");
330
331 structure_start = gst_structure_new ("dtmf-event",
332 "type", G_TYPE_INT, 1,
333 "number", G_TYPE_INT, param->number,
334 "volume", G_TYPE_INT, param->volume,
335 "start", G_TYPE_BOOLEAN, TRUE, NULL);
336
337 //gst_structure_set (structure, "number", G_TYPE_INT, num,
338 // "volume", G_TYPE_INT, volume, NULL);
339
340 event_start = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, structure_start);
341 gst_element_send_event (param->pipeline, event_start);
342
343 PRINT_DEFAULT("%s end, handle: 0x%x, number: %d, time_ms: %d, volume: %d \n",
344 __FUNCTION__, handle, param->number, param->time_ms, param->volume);
345
346 param->handle = handle;
347
348 return handle;
349}
350
351int dtmf_stop(DTMF_HANDLE handle)
352{
353 if (handle == NULL) {
354 PRINT_ERR ("%s, handle is NULL\n", __FUNCTION__);
355 return RET_FAIL;
356 }
357 DTMF_PARAM_T *param = (DTMF_PARAM_T *)handle;
358
359 PRINT_DEFAULT("%s start, DTMF_HANDLE: 0x%x, handle: 0x%x \n",
360 __FUNCTION__, handle, param->handle);
361
362 if (handle != param->handle) {
363 PRINT_DEFAULT("invalid handle: %p \n", handle);
364 return RET_OK;
365 }
366
367 g_main_loop_quit (param->loop);
368 pthread_join(param->thread, NULL);
369
370 PRINT_DEFAULT("%s end, handle: 0x%x \n", __FUNCTION__, handle);
371
372 return RET_OK;
373}