mirror of
https://github.com/jambonz/freeswitch-modules.git
synced 2025-12-19 08:37:44 +00:00
360 lines
13 KiB
C
360 lines
13 KiB
C
/*
|
|
*
|
|
* mod_audio_fork.c -- Freeswitch module for forking audio to remote server over websockets
|
|
*
|
|
*/
|
|
#include "mod_audio_fork.h"
|
|
#include "lws_glue.h"
|
|
|
|
//static int mod_running = 0;
|
|
|
|
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_audio_fork_shutdown);
|
|
SWITCH_MODULE_RUNTIME_FUNCTION(mod_audio_fork_runtime);
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_audio_fork_load);
|
|
|
|
SWITCH_MODULE_DEFINITION(mod_audio_fork, mod_audio_fork_load, mod_audio_fork_shutdown, NULL /*mod_audio_fork_runtime*/);
|
|
|
|
static void responseHandler(switch_core_session_t* session, const char * eventName, char * json) {
|
|
switch_event_t *event;
|
|
|
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
|
if (json) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "responseHandler: sending event payload: %s.\n", json);
|
|
switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, eventName);
|
|
switch_channel_event_set_data(channel, event);
|
|
if (json) switch_event_add_body(event, "%s", json);
|
|
switch_event_fire(&event);
|
|
}
|
|
|
|
static switch_bool_t capture_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
|
|
{
|
|
switch_core_session_t *session = switch_core_media_bug_get_session(bug);
|
|
switch (type) {
|
|
case SWITCH_ABC_TYPE_INIT:
|
|
break;
|
|
|
|
case SWITCH_ABC_TYPE_CLOSE:
|
|
{
|
|
private_t* tech_pvt = (private_t *) switch_core_media_bug_get_user_data(bug);
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Got SWITCH_ABC_TYPE_CLOSE for bug %s\n", tech_pvt->bugname);
|
|
fork_session_cleanup(session, tech_pvt->bugname, NULL, 1);
|
|
}
|
|
break;
|
|
|
|
case SWITCH_ABC_TYPE_READ:
|
|
return fork_frame(session, bug);
|
|
break;
|
|
|
|
case SWITCH_ABC_TYPE_WRITE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return SWITCH_TRUE;
|
|
}
|
|
|
|
static switch_status_t start_capture(switch_core_session_t *session,
|
|
switch_media_bug_flag_t flags,
|
|
char* host,
|
|
unsigned int port,
|
|
char* path,
|
|
int sampling,
|
|
int sslFlags,
|
|
char* bugname,
|
|
char* metadata)
|
|
{
|
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
|
switch_media_bug_t *bug;
|
|
switch_status_t status;
|
|
switch_codec_t* read_codec;
|
|
|
|
void *pUserData = NULL;
|
|
int channels = (flags & SMBF_STEREO) ? 2 : 1;
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
|
|
"mod_audio_fork (%s): streaming %d sampling to %s path %s port %d tls: %s.\n",
|
|
bugname, sampling, host, path, port, sslFlags ? "yes" : "no");
|
|
|
|
if (switch_channel_get_private(channel, bugname)) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "mod_audio_fork: bug %s already attached!\n", bugname);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
read_codec = switch_core_session_get_read_codec(session);
|
|
|
|
if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "mod_audio_fork: channel must have reached pre-answer status before calling start!\n");
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "calling fork_session_init.\n");
|
|
if (SWITCH_STATUS_FALSE == fork_session_init(session, responseHandler, read_codec->implementation->actual_samples_per_second,
|
|
host, port, path, sampling, sslFlags, channels, bugname, metadata, &pUserData)) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error initializing mod_audio_fork session.\n");
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "adding bug %s.\n", bugname);
|
|
if ((status = switch_core_media_bug_add(session, bugname, NULL, capture_callback, pUserData, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
|
|
return status;
|
|
}
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "setting bug private data %s.\n", bugname);
|
|
switch_channel_set_private(channel, bugname, bug);
|
|
|
|
if (fork_session_connect(&pUserData) != SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error mod_audio_fork session cannot connect.\n");
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "exiting start_capture.\n");
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
static switch_status_t do_stop(switch_core_session_t *session, char* bugname, char* text)
|
|
{
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
|
|
|
if (text) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "mod_audio_fork (%s): stop w/ final text %s\n", bugname, text);
|
|
}
|
|
else {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "mod_audio_fork (%s): stop\n", bugname);
|
|
}
|
|
status = fork_session_cleanup(session, bugname, text, 0);
|
|
|
|
return status;
|
|
}
|
|
|
|
static switch_status_t do_pauseresume(switch_core_session_t *session, char* bugname, int pause)
|
|
{
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "mod_audio_fork (%s): %s\n", bugname, pause ? "pause" : "resume");
|
|
status = fork_session_pauseresume(session, bugname, pause);
|
|
|
|
return status;
|
|
}
|
|
|
|
static switch_status_t do_graceful_shutdown(switch_core_session_t *session, char* bugname)
|
|
{
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "mod_audio_fork (%s): do_graceful_shutdown \n", bugname);
|
|
status = fork_session_graceful_shutdown(session, bugname);
|
|
|
|
return status;
|
|
}
|
|
|
|
static switch_status_t send_text(switch_core_session_t *session, char* bugname, char* text) {
|
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
|
|
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
|
switch_media_bug_t *bug = switch_channel_get_private(channel, bugname);
|
|
|
|
if (bug) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "mod_audio_fork (%s): sending text: %s.\n", bugname, text);
|
|
status = fork_session_send_text(session, bugname, text);
|
|
}
|
|
else {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "mod_audio_fork (%s): no bug, failed sending text: %s.\n", bugname, text);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
#define FORK_API_SYNTAX "<uuid> [start | stop | send_text | pause | resume | graceful-shutdown ] [wss-url | path] [mono | mixed | stereo] [8000 | 16000 | 24000 | 32000 | 64000] [bugname] [metadata]"
|
|
SWITCH_STANDARD_API(fork_function)
|
|
{
|
|
char *mycmd = NULL, *argv[7] = { 0 };
|
|
int argc = 0;
|
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
|
char *bugname = MY_BUG_NAME;
|
|
|
|
if (!zstr(cmd) && (mycmd = strdup(cmd))) {
|
|
argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
|
|
}
|
|
assert(cmd);
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "mod_audio_fork cmd: %s\n", cmd);
|
|
|
|
|
|
if (zstr(cmd) || argc < 2 ||
|
|
(0 == strcmp(argv[1], "start") && argc < 4)) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error with command %s %s %s.\n", cmd, argv[0], argv[1]);
|
|
stream->write_function(stream, "-USAGE: %s\n", FORK_API_SYNTAX);
|
|
goto done;
|
|
} else {
|
|
switch_core_session_t *lsession = NULL;
|
|
|
|
if ((lsession = switch_core_session_locate(argv[0]))) {
|
|
if (!strcasecmp(argv[1], "stop")) {
|
|
char * text = NULL;
|
|
if (argc > 3) {
|
|
bugname = argv[2];
|
|
text = argv[3];
|
|
}
|
|
else if (argc > 2) {
|
|
if (argv[2][0] == '{' || argv[2][0] == '[') text = argv[2];
|
|
else bugname = argv[2];
|
|
}
|
|
status = do_stop(lsession, bugname, text);
|
|
}
|
|
else if (!strcasecmp(argv[1], "pause")) {
|
|
if (argc > 2) bugname = argv[2];
|
|
status = do_pauseresume(lsession, bugname, 1);
|
|
}
|
|
else if (!strcasecmp(argv[1], "resume")) {
|
|
if (argc > 2) bugname = argv[2];
|
|
status = do_pauseresume(lsession, bugname, 0);
|
|
}
|
|
else if (!strcasecmp(argv[1], "graceful-shutdown")) {
|
|
if (argc > 2) bugname = argv[2];
|
|
status = do_graceful_shutdown(lsession, bugname);
|
|
}
|
|
else if (!strcasecmp(argv[1], "send_text")) {
|
|
char * text = 0;
|
|
if (argc < 3) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "send_text requires an argument specifying text to send\n");
|
|
switch_core_session_rwunlock(lsession);
|
|
goto done;
|
|
}
|
|
if (argc > 3) {
|
|
bugname = argv[2];
|
|
text = argv[3];
|
|
}
|
|
else {
|
|
if (argv[2][0] == '{' || argv[2][0] == '[') text = argv[2];
|
|
else bugname = argv[2];
|
|
}
|
|
status = send_text(lsession, bugname, text);
|
|
}
|
|
else if (!strcasecmp(argv[1], "start")) {
|
|
switch_channel_t *channel = switch_core_session_get_channel(lsession);
|
|
char host[MAX_WS_URL_LEN], path[MAX_PATH_LEN];
|
|
unsigned int port;
|
|
int sslFlags;
|
|
int sampling = 8000;
|
|
switch_media_bug_flag_t flags = SMBF_READ_STREAM ;
|
|
char *metadata = NULL;
|
|
if( argc > 6) {
|
|
bugname = argv[5];
|
|
metadata = argv[6];
|
|
}
|
|
else if (argc > 5) {
|
|
if (argv[5][0] == '{' || argv[5][0] == '[') metadata = argv[5];
|
|
else bugname = argv[5];
|
|
}
|
|
if (0 == strcmp(argv[3], "mixed")) {
|
|
flags |= SMBF_WRITE_STREAM ;
|
|
}
|
|
else if (0 == strcmp(argv[3], "stereo")) {
|
|
flags |= SMBF_WRITE_STREAM ;
|
|
flags |= SMBF_STEREO;
|
|
}
|
|
else if(0 != strcmp(argv[3], "mono")) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "invalid mix type: %s, must be mono, mixed, or stereo\n", argv[3]);
|
|
switch_core_session_rwunlock(lsession);
|
|
goto done;
|
|
}
|
|
if (0 == strcmp(argv[4], "16k")) {
|
|
sampling = 16000;
|
|
}
|
|
else if (0 == strcmp(argv[4], "8k")) {
|
|
sampling = 8000;
|
|
}
|
|
else {
|
|
sampling = atoi(argv[4]);
|
|
}
|
|
if (!parse_ws_uri(channel, argv[2], &host[0], &path[0], &port, &sslFlags)) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "invalid websocket uri: %s\n", argv[2]);
|
|
}
|
|
else if (sampling % 8000 != 0) {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "invalid sample rate: %s\n", argv[4]);
|
|
}
|
|
status = start_capture(lsession, flags, host, port, path, sampling, sslFlags, bugname, metadata);
|
|
}
|
|
else {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "unsupported mod_audio_fork cmd: %s\n", argv[1]);
|
|
}
|
|
switch_core_session_rwunlock(lsession);
|
|
}
|
|
else {
|
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error locating session %s\n", argv[0]);
|
|
}
|
|
}
|
|
|
|
if (status == SWITCH_STATUS_SUCCESS) {
|
|
stream->write_function(stream, "+OK Success\n");
|
|
} else {
|
|
stream->write_function(stream, "-ERR Operation Failed\n");
|
|
}
|
|
|
|
done:
|
|
|
|
switch_safe_free(mycmd);
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_audio_fork_load)
|
|
{
|
|
switch_api_interface_t *api_interface;
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "mod_audio_fork API loading..\n");
|
|
|
|
/* connect my internal structure to the blank pointer passed to me */
|
|
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
|
|
|
/* create/register custom event message types */
|
|
if (switch_event_reserve_subclass(EVENT_TRANSCRIPTION) != SWITCH_STATUS_SUCCESS ||
|
|
switch_event_reserve_subclass(EVENT_TRANSFER) != SWITCH_STATUS_SUCCESS ||
|
|
switch_event_reserve_subclass(EVENT_PLAY_AUDIO) != SWITCH_STATUS_SUCCESS ||
|
|
switch_event_reserve_subclass(EVENT_KILL_AUDIO) != SWITCH_STATUS_SUCCESS ||
|
|
switch_event_reserve_subclass(EVENT_ERROR) != SWITCH_STATUS_SUCCESS ||
|
|
switch_event_reserve_subclass(EVENT_DISCONNECT) != SWITCH_STATUS_SUCCESS) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register an event subclass for mod_audio_fork API.\n");
|
|
return SWITCH_STATUS_TERM;
|
|
}
|
|
|
|
SWITCH_ADD_API(api_interface, "uuid_audio_fork", "audio_fork API", fork_function, FORK_API_SYNTAX);
|
|
switch_console_set_complete("add uuid_audio_fork start wss-url metadata");
|
|
switch_console_set_complete("add uuid_audio_fork start wss-url");
|
|
switch_console_set_complete("add uuid_audio_fork stop");
|
|
|
|
fork_init();
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "mod_audio_fork API successfully loaded\n");
|
|
|
|
/* indicate that the module should continue to be loaded */
|
|
//mod_running = 1;
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
Called when the system shuts down
|
|
Macro expands to: switch_status_t mod_audio_fork_shutdown() */
|
|
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_audio_fork_shutdown)
|
|
{
|
|
fork_cleanup();
|
|
//mod_running = 0;
|
|
switch_event_free_subclass(EVENT_TRANSCRIPTION);
|
|
switch_event_free_subclass(EVENT_TRANSFER);
|
|
switch_event_free_subclass(EVENT_PLAY_AUDIO);
|
|
switch_event_free_subclass(EVENT_KILL_AUDIO);
|
|
switch_event_free_subclass(EVENT_DISCONNECT);
|
|
switch_event_free_subclass(EVENT_ERROR);
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
If it exists, this is called in it's own thread when the module-load completes
|
|
If it returns anything but SWITCH_STATUS_TERM it will be called again automatically
|
|
Macro expands to: switch_status_t mod_audio_fork_runtime()
|
|
*/
|
|
/*
|
|
SWITCH_MODULE_RUNTIME_FUNCTION(mod_audio_fork_runtime)
|
|
{
|
|
fork_service_threads(&mod_running);
|
|
return SWITCH_STATUS_TERM;
|
|
}
|
|
*/
|