blob: a1af20bdfb2604c74c33148e80e765ece1e5b306 [file] [log] [blame]
#include "dtmf.h"
#define USE_SYSLOG 1
#if USE_SYSLOG
#include <log/log.h>
#endif
#define RET_OK 0
#define RET_FAIL -1
#define CRITICAL 0
#define DEFAULT 1
#define INFO 2
#define DEBUGLEVEL DEFAULT
#define DTMF_TAG "[LIBDTMF]"
static guint dtmf_get_time_ms(guint base)
{
guint current = g_get_monotonic_time() / G_TIME_SPAN_MILLISECOND;
if (current >= base) {
return (current - base);
} else {
return (G_MAXUINT32 - (base - current));
}
}
#if USE_SYSLOG
#undef LOG_TAG
#define LOG_TAG "[LIBDTMF]"
#define PRINT_ERR(fmt, args...) RLOGE(fmt, ##args)
#define PRINT_DEFAULT(fmt, args...) RLOGI(fmt, ##args)
#define PRINT_INFO(fmt, args...) RLOGD(fmt, ##args)
#else
#define app_printf(level, fmt, args...) \
do { if ((level) <= DEBUGLEVEL) \
{ fprintf (stderr, fmt, ##args); } } while (0)
#define PRINT_ERR(fmt, args...) \
app_printf(CRITICAL, DTMF_TAG"[ERR][L%d][T%d]"fmt, __LINE__, dtmf_get_time_ms(0),##args);
#define PRINT_DEFAULT(fmt, args...) \
app_printf(DEFAULT, DTMF_TAG"[DEF][L%d][T%d]"fmt, __LINE__, dtmf_get_time_ms(0), ##args);
#define PRINT_INFO(fmt, args...) \
app_printf(INFO, DTMF_TAG"[INFO][L%d][T%d]"fmt, __LINE__, dtmf_get_time_ms(0), ##args);
#endif
#define FILE_NAME_MAX_LENGTH 64
#define DTMFSRC_MUTE_LENGTH 100 //unit: ms, from the practice result, the begining 100ms data is 0.
typedef struct {
DTMF_HANDLE handle;
gint number; //the event number: 0-15
gint volume; //db value: 0-36
gint time_ms; //the duration of tone, unit: ms
pthread_t thread;
GMainLoop *loop;
GstElement *pipeline;
guint bus_watch_id;
GstState gst_cur_state;
gint output; //0: output to pulsesink; 1: output the buffer pointer
char *path; //while output==1, file path to store the tone data
//FILE *fd; // file handle to the output file
} DTMF_PARAM_T;
static int dtmf_stop_full(DTMF_PARAM_T *param)
{
GMainLoop *loop = param->loop;
GstElement *pipeline = param->pipeline;
guint bus_watch_id = param->bus_watch_id;
GstStructure *structure_stop = NULL;
GstEvent *event_stop = NULL;
PRINT_DEFAULT("%s start, param: 0x%x, handle: 0x%x, number: %d, time_ms: %d, volume: %d \n",
__FUNCTION__, param, param->handle, param->number, param->time_ms, param->volume);
if (param != param->handle) {
PRINT_DEFAULT("invalid handle: %p \n", param->handle);
return RET_OK;
}
structure_stop = gst_structure_new ("dtmf-event",
"type", G_TYPE_INT, 1,
"number", G_TYPE_INT, 1,
"volume", G_TYPE_INT, 25,
"start", G_TYPE_BOOLEAN, FALSE, NULL);
event_stop = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, structure_stop);
gst_element_send_event (pipeline, event_stop);
PRINT_DEFAULT("gst_element_set_state NULL start \n");
gst_element_set_state (pipeline, GST_STATE_NULL);
PRINT_DEFAULT("gst_element_set_state NULL end \n");
gst_object_unref (GST_OBJECT (pipeline));
g_source_remove (bus_watch_id);
g_main_loop_unref (loop);
param->handle = NULL;
free(param);
param = NULL;
PRINT_DEFAULT("%s end \n", __FUNCTION__);
return RET_OK;
}
static gboolean dtmf_bus_call (GstBus *bus, GstMessage *msg, gpointer data)
{
DTMF_PARAM_T *param = (DTMF_PARAM_T *)data;
GstState oldstate, newstate, pending;
gchar *debug;
GError *error;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_EOS:
PRINT_DEFAULT ("Receive GST_MESSAGE_EOS \n");
g_main_loop_quit (param->loop);
break;
case GST_MESSAGE_STATE_CHANGED:
gst_message_parse_state_changed (msg, &oldstate, &newstate, &pending);
param->gst_cur_state = newstate;
PRINT_INFO ("GST_MESSAGE_STATE_CHANGED, oldstate[%d], newstate[%d], pending[%d] \n",
oldstate, newstate, pending);
break;
case GST_MESSAGE_ERROR:
gst_message_parse_error (msg, &error, &debug);
g_free (debug);
PRINT_ERR ("Error: %s\n", error->message);
g_error_free (error);
g_main_loop_quit (param->loop);
break;
default:
break;
}
return TRUE;
}
#if 0
void dtmf_wait_ms(gint ms)
{
gint err;
struct timeval wait_tv;
wait_tv.tv_sec = ms / 1000;
wait_tv.tv_usec = (ms % 1000) * 1000;
do {
err = select(0, NULL, NULL, NULL, &wait_tv);
} while(err<0 && errno == EINTR);
}
static GstFlowReturn dtmf_tone_data_output(GstElement *sink, void *data)
{
DTMF_PARAM_T *param = (DTMF_PARAM_T *)data;
GstSample *sample;
GstBuffer *buffer;
GstMapInfo map;
sample = gst_app_sink_pull_sample (GST_APP_SINK (sink));
if (!sample)
{
PRINT_ERR("could not get sample. \n");
return GST_FLOW_ERROR;
}
buffer = gst_sample_get_buffer (sample);
gst_buffer_map (buffer, &map, GST_MAP_READ);
PRINT_INFO ("tone data: size=%d, data=%p \n", map.size, map.data);
fwrite(map.data, 1, map.size, param->fd);
gst_buffer_unmap (buffer, &map);
gst_sample_unref (sample);
return GST_FLOW_OK;
}
#endif
static gint dtmf_check_param(DTMF_PARAM_T *param)
{
if ((param->number < 0) || (param->number > 15)) {
PRINT_ERR("invalid number: %d, valid value is [0,15] \n ", param->number);
return RET_FAIL;
}
if ((param->time_ms != 0) && (param->time_ms < DTMF_MIN_LENGTH)) {
PRINT_ERR("invalid time_ms: %d \n ", param->time_ms);
return RET_FAIL;
}
if ((param->volume < 0) || (param->volume > 36)) {
PRINT_ERR("invalid volume: %d, valid value is [0,36] \n ", param->volume);
return RET_FAIL;
}
if ((param->output < 0) || (param->output > 1)) {
PRINT_ERR("invalid output: %d, valid value is [0,1] \n ", param->output);
return RET_FAIL;
}
if ((param->output == 1) && (param->path == NULL)) {
PRINT_ERR("path is NULL while output is filesink \n ");
return RET_FAIL;
}
return RET_OK;
}
void* dtmf_thread_func(void *arg)
{
DTMF_PARAM_T *param = (DTMF_PARAM_T *)arg;
PRINT_DEFAULT("%s start \n", __FUNCTION__);
g_main_loop_run (param->loop);
PRINT_DEFAULT("g_main_loop_run end \n");
dtmf_stop_full(param);
PRINT_DEFAULT("%s end \n", __FUNCTION__);
return ((void *)0);
}
DTMF_HANDLE dtmf_start(gint num, gint time_ms, gint volume, DTMF_EXTRA *extra)
{
GMainLoop *loop;
GstElement *pipeline, *source, *sink;
GstBus *bus;
guint bus_watch_id;
pthread_t thread;
GstStructure *structure_start = NULL;
GstEvent *event_start = NULL;
DTMF_HANDLE handle = NULL;
DTMF_PARAM_T *param = malloc(sizeof(DTMF_PARAM_T));
if (param == NULL) {
PRINT_ERR("malloc DTMF_PARAM_T fail \n");
}
memset(param, 0, sizeof(DTMF_PARAM_T));
handle = (DTMF_HANDLE)param;
//param->handle = handle;
param->number = num;
param->time_ms = time_ms;
param->volume = volume;
PRINT_DEFAULT("%s start, handle: 0x%x, number: %d, time_ms: %d, volume: %d \n ",
__FUNCTION__, handle, param->number, param->time_ms, param->volume);
if (extra != NULL) {
param->output = extra->output;
param->path = extra->path;
PRINT_DEFAULT("output: %d, path: %s \n", extra->output, extra->path);
}
if (dtmf_check_param(param) < 0) {
PRINT_ERR("input parameter is invalid! \n");
return NULL;
}
gst_init (NULL, NULL);
loop = g_main_loop_new (NULL, FALSE);
pipeline = gst_pipeline_new ("dtmf-pipeline");
source = gst_element_factory_make ("dtmfsrc", "dtmf-file-source");
switch (param->output) {
case 0:
sink = gst_element_factory_make ("pulsesink", "dtmf-audio-output");
break;
case 1:
sink = gst_element_factory_make ("filesink", "dtmf-audio-output");
g_object_set(sink, "location", extra->path, NULL);
break;
default:
PRINT_ERR("input parameter 'output' is invalid! \n");
break;
}
//sink = gst_element_factory_make ("appsink", "audio-output-buffer");
//g_object_set(sink, "emit-signals", TRUE, "sync", TRUE, NULL);
//g_signal_connect(sink, "new-sample", G_CALLBACK(tone_data_output), (gpointer)param);
if (!pipeline || !source || !sink) {
PRINT_ERR ("One element creat fail, pipeline: %p, source: %p, sink: %p \n",
pipeline, source, sink);
free(param);
return NULL;
}
guint interval = 50;
gint buffers_num = -1;
if (time_ms > 0) {
// dtmfsrc plugin will send EOS when the number of buffers reach 'buffers_num' .
g_object_get(source, "interval", &interval, NULL);
buffers_num = (time_ms + DTMFSRC_MUTE_LENGTH) / interval;
PRINT_DEFAULT("dtmfsrc interval: %d ms, set num-buffers: %d \n",
interval, buffers_num);
g_object_set(source, "num-buffers", buffers_num, NULL);
}
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
bus_watch_id = gst_bus_add_watch (bus, dtmf_bus_call, (gpointer)param);
gst_object_unref (bus);
param->pipeline = pipeline;
param->loop = loop;
param->bus_watch_id = bus_watch_id;
pthread_create (&thread, NULL, (void *)dtmf_thread_func, param);
param->thread = thread;
gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
gst_element_link_many (source, sink, NULL);
gst_element_set_state (param->pipeline, GST_STATE_PLAYING);
PRINT_INFO("wait GST_STATE_PLAYING start \n");
do {
if (param->gst_cur_state == GST_STATE_PLAYING)
break;
else
usleep(5000);
} while (1);
PRINT_INFO("wait GST_STATE_PLAYING end \n");
structure_start = gst_structure_new ("dtmf-event",
"type", G_TYPE_INT, 1,
"number", G_TYPE_INT, param->number,
"volume", G_TYPE_INT, param->volume,
"start", G_TYPE_BOOLEAN, TRUE, NULL);
//gst_structure_set (structure, "number", G_TYPE_INT, num,
// "volume", G_TYPE_INT, volume, NULL);
event_start = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, structure_start);
gst_element_send_event (param->pipeline, event_start);
PRINT_DEFAULT("%s end, handle: 0x%x, number: %d, time_ms: %d, volume: %d \n",
__FUNCTION__, handle, param->number, param->time_ms, param->volume);
param->handle = handle;
return handle;
}
int dtmf_stop(DTMF_HANDLE handle)
{
if (handle == NULL) {
PRINT_ERR ("%s, handle is NULL\n", __FUNCTION__);
return RET_FAIL;
}
DTMF_PARAM_T *param = (DTMF_PARAM_T *)handle;
PRINT_DEFAULT("%s start, DTMF_HANDLE: 0x%x, handle: 0x%x \n",
__FUNCTION__, handle, param->handle);
if (handle != param->handle) {
PRINT_DEFAULT("invalid handle: %p \n", handle);
return RET_OK;
}
g_main_loop_quit (param->loop);
pthread_join(param->thread, NULL);
PRINT_DEFAULT("%s end, handle: 0x%x \n", __FUNCTION__, handle);
return RET_OK;
}