blob: a1af20bdfb2604c74c33148e80e765ece1e5b306 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +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, *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
338int 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}