Thu Jul 9 13:40:18 2009

Asterisk developer's documentation


app_jack.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2007 - 2008, Russell Bryant
00005  *
00006  * Russell Bryant <russell@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \brief Jack Application
00022  *
00023  * \author Russell Bryant <russell@digium.com>
00024  *
00025  * This is an application to connect an Asterisk channel to an input
00026  * and output jack port so that the audio can be processed through
00027  * another application, or to play audio from another application.
00028  *
00029  * \arg http://www.jackaudio.org/
00030  *
00031  * \note To install libresample, check it out of the following repository:
00032  * <code>$ svn co http://svn.digium.com/svn/thirdparty/libresample/trunk</code>
00033  *
00034  * \ingroup applications
00035  */
00036 
00037 /*** MODULEINFO
00038    <depend>jack</depend>
00039    <depend>resample</depend>
00040  ***/
00041 
00042 #include "asterisk.h"
00043 
00044 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 140567 $")
00045 
00046 #include <limits.h>
00047 
00048 #include <jack/jack.h>
00049 #include <jack/ringbuffer.h>
00050 
00051 #include <libresample.h>
00052 
00053 #include "asterisk/module.h"
00054 #include "asterisk/channel.h"
00055 #include "asterisk/strings.h"
00056 #include "asterisk/lock.h"
00057 #include "asterisk/app.h"
00058 #include "asterisk/pbx.h"
00059 #include "asterisk/audiohook.h"
00060 
00061 #define RESAMPLE_QUALITY 1
00062 
00063 #define RINGBUFFER_SIZE 16384
00064 
00065 /*! \brief Common options between the Jack() app and JACK_HOOK() function */
00066 #define COMMON_OPTIONS \
00067 "    s(<name>) - Connect to the specified jack server name.\n" \
00068 "    i(<name>) - Connect the output port that gets created to the specified\n" \
00069 "                jack input port.\n" \
00070 "    o(<name>) - Connect the input port that gets created to the specified\n" \
00071 "                jack output port.\n" \
00072 "    n         - Do not automatically start the JACK server if it is not already\n" \
00073 "                running.\n"
00074 
00075 static char *jack_app = "JACK";
00076 static char *jack_synopsis = 
00077 "JACK (Jack Audio Connection Kit) Application";
00078 static char *jack_desc = 
00079 "JACK([options])\n"
00080 "  When this application is executed, two jack ports will be created; one input\n"
00081 "and one output.  Other applications can be hooked up to these ports to access\n"
00082 "the audio coming from, or being sent to the channel.\n"
00083 "  Valid options:\n"
00084 COMMON_OPTIONS
00085 "";
00086 
00087 struct jack_data {
00088    AST_DECLARE_STRING_FIELDS(
00089       AST_STRING_FIELD(server_name);
00090       AST_STRING_FIELD(connect_input_port);
00091       AST_STRING_FIELD(connect_output_port);
00092    );
00093    jack_client_t *client;
00094    jack_port_t *input_port;
00095    jack_port_t *output_port;
00096    jack_ringbuffer_t *input_rb;
00097    jack_ringbuffer_t *output_rb;
00098    void *output_resampler;
00099    double output_resample_factor;
00100    void *input_resampler;
00101    double input_resample_factor;
00102    unsigned int stop:1;
00103    unsigned int has_audiohook:1;
00104    unsigned int no_start_server:1;
00105    /*! Only used with JACK_HOOK */
00106    struct ast_audiohook audiohook;
00107 };
00108 
00109 static const struct {
00110    jack_status_t status;
00111    const char *str;
00112 } jack_status_table[] = {
00113    { JackFailure,        "Failure" },
00114    { JackInvalidOption,  "Invalid Option" },
00115    { JackNameNotUnique,  "Name Not Unique" },
00116    { JackServerStarted,  "Server Started" },
00117    { JackServerFailed,   "Server Failed" },
00118    { JackServerError,    "Server Error" },
00119    { JackNoSuchClient,   "No Such Client" },
00120    { JackLoadFailure,    "Load Failure" },
00121    { JackInitFailure,    "Init Failure" },
00122    { JackShmFailure,     "Shared Memory Access Failure" },
00123    { JackVersionError,   "Version Mismatch" },
00124 };
00125 
00126 static const char *jack_status_to_str(jack_status_t status)
00127 {
00128    int i;
00129 
00130    for (i = 0; i < ARRAY_LEN(jack_status_table); i++) {
00131       if (jack_status_table[i].status == status)
00132          return jack_status_table[i].str;
00133    }
00134 
00135    return "Unknown Error";
00136 }
00137 
00138 static void log_jack_status(const char *prefix, jack_status_t status)
00139 {
00140    struct ast_str *str = ast_str_alloca(512);
00141    int i, first = 0;
00142 
00143    for (i = 0; i < (sizeof(status) * 8); i++) {
00144       if (!(status & (1 << i)))
00145          continue;
00146 
00147       if (!first) {
00148          ast_str_set(&str, 0, "%s", jack_status_to_str((1 << i)));
00149          first = 1;
00150       } else
00151          ast_str_append(&str, 0, ", %s", jack_status_to_str((1 << i)));
00152    }
00153    
00154    ast_log(LOG_NOTICE, "%s: %s\n", prefix, str->str);
00155 }
00156 
00157 static int alloc_resampler(struct jack_data *jack_data, int input)
00158 {
00159    double from_srate, to_srate, jack_srate;
00160    void **resampler;
00161    double *resample_factor;
00162 
00163    if (input && jack_data->input_resampler)
00164       return 0;
00165 
00166    if (!input && jack_data->output_resampler)
00167       return 0;
00168 
00169    jack_srate = jack_get_sample_rate(jack_data->client);
00170 
00171    /* XXX Hard coded 8 kHz */
00172 
00173    to_srate = input ? 8000.0 : jack_srate; 
00174    from_srate = input ? jack_srate : 8000.0;
00175 
00176    resample_factor = input ? &jack_data->input_resample_factor : 
00177       &jack_data->output_resample_factor;
00178 
00179    if (from_srate == to_srate) {
00180       /* Awesome!  The jack sample rate is the same as ours.
00181        * Resampling isn't needed. */
00182       *resample_factor = 1.0;
00183       return 0;
00184    }
00185 
00186    *resample_factor = to_srate / from_srate;
00187 
00188    resampler = input ? &jack_data->input_resampler :
00189       &jack_data->output_resampler;
00190 
00191    if (!(*resampler = resample_open(RESAMPLE_QUALITY, 
00192       *resample_factor, *resample_factor))) {
00193       ast_log(LOG_ERROR, "Failed to open %s resampler\n", 
00194          input ? "input" : "output");
00195       return -1;
00196    }
00197 
00198    return 0;
00199 }
00200 
00201 /*!
00202  * \brief Handle jack input port
00203  *
00204  * Read nframes number of samples from the input buffer, resample it
00205  * if necessary, and write it into the appropriate ringbuffer. 
00206  */
00207 static void handle_input(void *buf, jack_nframes_t nframes, 
00208    struct jack_data *jack_data)
00209 {
00210    short s_buf[nframes];
00211    float *in_buf = buf;
00212    size_t res;
00213    int i;
00214    size_t write_len = sizeof(s_buf);
00215 
00216    if (jack_data->input_resampler) {
00217       int total_in_buf_used = 0;
00218       int total_out_buf_used = 0;
00219       float f_buf[nframes + 1];
00220 
00221       memset(f_buf, 0, sizeof(f_buf));
00222 
00223       while (total_in_buf_used < nframes) {
00224          int in_buf_used;
00225          int out_buf_used;
00226 
00227          out_buf_used = resample_process(jack_data->input_resampler,
00228             jack_data->input_resample_factor,
00229             &in_buf[total_in_buf_used], nframes - total_in_buf_used,
00230             0, &in_buf_used,
00231             &f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);
00232 
00233          if (out_buf_used < 0)
00234             break;
00235 
00236          total_out_buf_used += out_buf_used;
00237          total_in_buf_used += in_buf_used;
00238    
00239          if (total_out_buf_used == ARRAY_LEN(f_buf)) {
00240             ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size, "
00241                "nframes '%d', total_out_buf_used '%d'\n", nframes, total_out_buf_used);
00242             break;
00243          }
00244       }
00245 
00246       for (i = 0; i < total_out_buf_used; i++)
00247          s_buf[i] = f_buf[i] * (SHRT_MAX / 1.0);
00248       
00249       write_len = total_out_buf_used * sizeof(int16_t);
00250    } else {
00251       /* No resampling needed */
00252 
00253       for (i = 0; i < nframes; i++)
00254          s_buf[i] = in_buf[i] * (SHRT_MAX / 1.0);
00255    }
00256 
00257    res = jack_ringbuffer_write(jack_data->input_rb, (const char *) s_buf, write_len);
00258    if (res != write_len) {
00259       ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
00260          (int) sizeof(s_buf), (int) res);
00261    }
00262 }
00263 
00264 /*!
00265  * \brief Handle jack output port
00266  *
00267  * Read nframes number of samples from the ringbuffer and write it out to the
00268  * output port buffer.
00269  */
00270 static void handle_output(void *buf, jack_nframes_t nframes, 
00271    struct jack_data *jack_data)
00272 {
00273    size_t res, len;
00274 
00275    len = nframes * sizeof(float);
00276 
00277    res = jack_ringbuffer_read(jack_data->output_rb, buf, len);
00278 
00279    if (len != res) {
00280       ast_debug(2, "Wanted %d bytes to send to the output port, "
00281          "but only got %d\n", (int) len, (int) res);
00282    }
00283 }
00284 
00285 static int jack_process(jack_nframes_t nframes, void *arg)
00286 {
00287    struct jack_data *jack_data = arg;
00288    void *input_port_buf, *output_port_buf;
00289 
00290    if (!jack_data->input_resample_factor)
00291       alloc_resampler(jack_data, 1);
00292 
00293    input_port_buf = jack_port_get_buffer(jack_data->input_port, nframes);
00294    handle_input(input_port_buf, nframes, jack_data);
00295 
00296    output_port_buf = jack_port_get_buffer(jack_data->output_port, nframes);
00297    handle_output(output_port_buf, nframes, jack_data);
00298 
00299    return 0;
00300 }
00301 
00302 static void jack_shutdown(void *arg)
00303 {
00304    struct jack_data *jack_data = arg;
00305 
00306    jack_data->stop = 1;
00307 }
00308 
00309 static struct jack_data *destroy_jack_data(struct jack_data *jack_data)
00310 {
00311    if (jack_data->input_port) {
00312       jack_port_unregister(jack_data->client, jack_data->input_port);
00313       jack_data->input_port = NULL;
00314    }
00315 
00316    if (jack_data->output_port) {
00317       jack_port_unregister(jack_data->client, jack_data->output_port);
00318       jack_data->output_port = NULL;
00319    }
00320 
00321    if (jack_data->client) {
00322       jack_client_close(jack_data->client);
00323       jack_data->client = NULL;
00324    }
00325 
00326    if (jack_data->input_rb) {
00327       jack_ringbuffer_free(jack_data->input_rb);
00328       jack_data->input_rb = NULL;
00329    }
00330 
00331    if (jack_data->output_rb) {
00332       jack_ringbuffer_free(jack_data->output_rb);
00333       jack_data->output_rb = NULL;
00334    }
00335 
00336    if (jack_data->output_resampler) {
00337       resample_close(jack_data->output_resampler);
00338       jack_data->output_resampler = NULL;
00339    }
00340    
00341    if (jack_data->input_resampler) {
00342       resample_close(jack_data->input_resampler);
00343       jack_data->input_resampler = NULL;
00344    }
00345 
00346    if (jack_data->has_audiohook)
00347       ast_audiohook_destroy(&jack_data->audiohook);
00348 
00349    ast_string_field_free_memory(jack_data);
00350 
00351    ast_free(jack_data);
00352 
00353    return NULL;
00354 }
00355 
00356 static int init_jack_data(struct ast_channel *chan, struct jack_data *jack_data)
00357 {
00358    const char *chan_name;
00359    jack_status_t status = 0;
00360    jack_options_t jack_options = JackNullOption;
00361 
00362    ast_channel_lock(chan);
00363    chan_name = ast_strdupa(chan->name);
00364    ast_channel_unlock(chan);
00365 
00366    if (!(jack_data->output_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
00367       return -1;
00368 
00369    if (!(jack_data->input_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
00370       return -1;
00371 
00372    if (jack_data->no_start_server)
00373       jack_options |= JackNoStartServer;
00374 
00375    if (!ast_strlen_zero(jack_data->server_name)) {
00376       jack_options |= JackServerName;
00377       jack_data->client = jack_client_open(chan_name, jack_options, &status,
00378          jack_data->server_name);
00379    } else {
00380       jack_data->client = jack_client_open(chan_name, jack_options, &status);
00381    }
00382 
00383    if (status)
00384       log_jack_status("Client Open Status", status);
00385 
00386    if (!jack_data->client)
00387       return -1;
00388 
00389    jack_data->input_port = jack_port_register(jack_data->client, "input",
00390       JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0);
00391    if (!jack_data->input_port) {
00392       ast_log(LOG_ERROR, "Failed to create input port for jack port\n");
00393       return -1;
00394    }
00395 
00396    jack_data->output_port = jack_port_register(jack_data->client, "output",
00397       JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0);
00398    if (!jack_data->output_port) {
00399       ast_log(LOG_ERROR, "Failed to create output port for jack port\n");
00400       return -1;
00401    }
00402 
00403    if (jack_set_process_callback(jack_data->client, jack_process, jack_data)) {
00404       ast_log(LOG_ERROR, "Failed to register process callback with jack client\n");
00405       return -1;
00406    }
00407 
00408    jack_on_shutdown(jack_data->client, jack_shutdown, jack_data);
00409 
00410    if (jack_activate(jack_data->client)) {
00411       ast_log(LOG_ERROR, "Unable to activate jack client\n");
00412       return -1;
00413    }
00414 
00415    while (!ast_strlen_zero(jack_data->connect_input_port)) {
00416       const char **ports;
00417       int i;
00418 
00419       ports = jack_get_ports(jack_data->client, jack_data->connect_input_port,
00420          NULL, JackPortIsInput);
00421 
00422       if (!ports) {
00423          ast_log(LOG_ERROR, "No input port matching '%s' was found\n",
00424             jack_data->connect_input_port);
00425          break;
00426       }
00427 
00428       for (i = 0; ports[i]; i++) {
00429          ast_debug(1, "Found port '%s' that matched specified input port '%s'\n",
00430             ports[i], jack_data->connect_input_port);
00431       }
00432 
00433       if (jack_connect(jack_data->client, jack_port_name(jack_data->output_port), ports[0])) {
00434          ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
00435             jack_port_name(jack_data->output_port));
00436       } else {
00437          ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
00438             jack_port_name(jack_data->output_port));
00439       }
00440 
00441       free((void *) ports);
00442 
00443       break;
00444    }
00445 
00446    while (!ast_strlen_zero(jack_data->connect_output_port)) {
00447       const char **ports;
00448       int i;
00449 
00450       ports = jack_get_ports(jack_data->client, jack_data->connect_output_port,
00451          NULL, JackPortIsOutput);
00452 
00453       if (!ports) {
00454          ast_log(LOG_ERROR, "No output port matching '%s' was found\n",
00455             jack_data->connect_output_port);
00456          break;
00457       }
00458 
00459       for (i = 0; ports[i]; i++) {
00460          ast_debug(1, "Found port '%s' that matched specified output port '%s'\n",
00461             ports[i], jack_data->connect_output_port);
00462       }
00463 
00464       if (jack_connect(jack_data->client, ports[0], jack_port_name(jack_data->input_port))) {
00465          ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
00466             jack_port_name(jack_data->input_port));
00467       } else {
00468          ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
00469             jack_port_name(jack_data->input_port));
00470       }
00471 
00472       free((void *) ports);
00473 
00474       break;
00475    }
00476 
00477    return 0;
00478 }
00479 
00480 static int queue_voice_frame(struct jack_data *jack_data, struct ast_frame *f)
00481 {
00482    float f_buf[f->samples * 8];
00483    size_t f_buf_used = 0;
00484    int i;
00485    int16_t *s_buf = f->data;
00486    size_t res;
00487 
00488    memset(f_buf, 0, sizeof(f_buf));
00489 
00490    if (!jack_data->output_resample_factor)
00491       alloc_resampler(jack_data, 0);
00492 
00493    if (jack_data->output_resampler) {
00494       float in_buf[f->samples];
00495       int total_in_buf_used = 0;
00496       int total_out_buf_used = 0;
00497 
00498       memset(in_buf, 0, sizeof(in_buf));
00499 
00500       for (i = 0; i < f->samples; i++)
00501          in_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);
00502 
00503       while (total_in_buf_used < ARRAY_LEN(in_buf)) {
00504          int in_buf_used;
00505          int out_buf_used;
00506 
00507          out_buf_used = resample_process(jack_data->output_resampler, 
00508             jack_data->output_resample_factor,
00509             &in_buf[total_in_buf_used], ARRAY_LEN(in_buf) - total_in_buf_used, 
00510             0, &in_buf_used, 
00511             &f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);
00512 
00513          if (out_buf_used < 0)
00514             break;
00515 
00516          total_out_buf_used += out_buf_used;
00517          total_in_buf_used += in_buf_used;
00518 
00519          if (total_out_buf_used == ARRAY_LEN(f_buf)) {
00520             ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size\n");
00521             break;
00522          }
00523       }
00524 
00525       f_buf_used = total_out_buf_used;
00526       if (f_buf_used > ARRAY_LEN(f_buf))
00527          f_buf_used = ARRAY_LEN(f_buf);
00528    } else {
00529       /* No resampling needed */
00530 
00531       for (i = 0; i < f->samples; i++)
00532          f_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);
00533 
00534       f_buf_used = f->samples;
00535    }
00536 
00537    res = jack_ringbuffer_write(jack_data->output_rb, (const char *) f_buf, f_buf_used * sizeof(float));
00538    if (res != (f_buf_used * sizeof(float))) {
00539       ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
00540          (int) (f_buf_used * sizeof(float)), (int) res);
00541    }
00542 
00543    return 0;
00544 }
00545 
00546 /*!
00547  * \brief handle jack audio
00548  *
00549  * \param[in]  chan The Asterisk channel to write the frames to if no output frame
00550  *             is provided.
00551  * \param[in]  jack_data This is the jack_data struct that contains the input
00552  *             ringbuffer that audio will be read from.
00553  * \param[out] out_frame If this argument is non-NULL, then assuming there is
00554  *             enough data avilable in the ringbuffer, the audio in this frame
00555  *             will get replaced with audio from the input buffer.  If there is
00556  *             not enough data available to read at this time, then the frame
00557  *             data gets zeroed out.
00558  *
00559  * Read data from the input ringbuffer, which is the properly resampled audio
00560  * that was read from the jack input port.  Write it to the channel in 20 ms frames,
00561  * or fill up an output frame instead if one is provided.
00562  *
00563  * \return Nothing.
00564  */
00565 static void handle_jack_audio(struct ast_channel *chan, struct jack_data *jack_data,
00566    struct ast_frame *out_frame)
00567 {  
00568    short buf[160];
00569    struct ast_frame f = {
00570       .frametype = AST_FRAME_VOICE,
00571       .subclass = AST_FORMAT_SLINEAR,
00572       .src = "JACK",
00573       .data = buf,
00574       .datalen = sizeof(buf),
00575       .samples = ARRAY_LEN(buf),
00576    };
00577 
00578    for (;;) {
00579       size_t res, read_len;
00580       char *read_buf;
00581 
00582       read_len = out_frame ? out_frame->datalen : sizeof(buf);
00583       read_buf = out_frame ? out_frame->data : buf;
00584 
00585       res = jack_ringbuffer_read_space(jack_data->input_rb);
00586 
00587       if (res < read_len) {
00588          /* Not enough data ready for another frame, move on ... */
00589          if (out_frame) {
00590             ast_debug(1, "Sending an empty frame for the JACK_HOOK\n");
00591             memset(out_frame->data, 0, out_frame->datalen);
00592          }
00593          break;
00594       }
00595 
00596       res = jack_ringbuffer_read(jack_data->input_rb, (char *) read_buf, read_len);
00597 
00598       if (res < read_len) {
00599          ast_log(LOG_ERROR, "Error reading from ringbuffer, even though it said there was enough data\n");
00600          break;
00601       }
00602 
00603       if (out_frame) {
00604          /* If an output frame was provided, then we just want to fill up the
00605           * buffer in that frame and return. */
00606          break;
00607       }
00608 
00609       ast_write(chan, &f);
00610    }
00611 }
00612 
00613 enum {
00614    OPT_SERVER_NAME =    (1 << 0),
00615    OPT_INPUT_PORT =     (1 << 1),
00616    OPT_OUTPUT_PORT =    (1 << 2),
00617    OPT_NOSTART_SERVER = (1 << 3),
00618 };
00619 
00620 enum {
00621    OPT_ARG_SERVER_NAME,
00622    OPT_ARG_INPUT_PORT,
00623    OPT_ARG_OUTPUT_PORT,
00624    /* Must be the last element */
00625    OPT_ARG_ARRAY_SIZE,
00626 };
00627 
00628 AST_APP_OPTIONS(jack_exec_options, BEGIN_OPTIONS
00629    AST_APP_OPTION_ARG('s', OPT_SERVER_NAME, OPT_ARG_SERVER_NAME),
00630    AST_APP_OPTION_ARG('i', OPT_INPUT_PORT, OPT_ARG_INPUT_PORT),
00631    AST_APP_OPTION_ARG('o', OPT_OUTPUT_PORT, OPT_ARG_OUTPUT_PORT),
00632    AST_APP_OPTION('n', OPT_NOSTART_SERVER),
00633 END_OPTIONS );
00634 
00635 static struct jack_data *jack_data_alloc(void)
00636 {
00637    struct jack_data *jack_data;
00638 
00639    if (!(jack_data = ast_calloc(1, sizeof(*jack_data))))
00640       return NULL;
00641    
00642    if (ast_string_field_init(jack_data, 32)) {
00643       ast_free(jack_data);
00644       return NULL;
00645    }
00646 
00647    return jack_data;
00648 }
00649 
00650 /*!
00651  * \note This must be done before calling init_jack_data().
00652  */
00653 static int handle_options(struct jack_data *jack_data, const char *__options_str)
00654 {
00655    struct ast_flags options = { 0, };
00656    char *option_args[OPT_ARG_ARRAY_SIZE];
00657    char *options_str;
00658 
00659    options_str = ast_strdupa(__options_str);
00660 
00661    ast_app_parse_options(jack_exec_options, &options, option_args, options_str);
00662 
00663    if (ast_test_flag(&options, OPT_SERVER_NAME)) {
00664       if (!ast_strlen_zero(option_args[OPT_ARG_SERVER_NAME]))
00665          ast_string_field_set(jack_data, server_name, option_args[OPT_ARG_SERVER_NAME]);
00666       else {
00667          ast_log(LOG_ERROR, "A server name must be provided with the s() option\n");
00668          return -1;
00669       }
00670    }
00671 
00672    if (ast_test_flag(&options, OPT_INPUT_PORT)) {
00673       if (!ast_strlen_zero(option_args[OPT_ARG_INPUT_PORT]))
00674          ast_string_field_set(jack_data, connect_input_port, option_args[OPT_ARG_INPUT_PORT]);
00675       else {
00676          ast_log(LOG_ERROR, "A name must be provided with the i() option\n");
00677          return -1;
00678       }
00679    }
00680 
00681    if (ast_test_flag(&options, OPT_OUTPUT_PORT)) {
00682       if (!ast_strlen_zero(option_args[OPT_ARG_OUTPUT_PORT]))
00683          ast_string_field_set(jack_data, connect_output_port, option_args[OPT_ARG_OUTPUT_PORT]);
00684       else {
00685          ast_log(LOG_ERROR, "A name must be provided with the o() option\n");
00686          return -1;
00687       }
00688    }
00689 
00690    jack_data->no_start_server = ast_test_flag(&options, OPT_NOSTART_SERVER) ? 1 : 0;
00691 
00692    return 0;
00693 }
00694 
00695 static int jack_exec(struct ast_channel *chan, void *data)
00696 {
00697    struct jack_data *jack_data;
00698    AST_DECLARE_APP_ARGS(args,
00699       AST_APP_ARG(options);
00700    );
00701 
00702    if (!(jack_data = jack_data_alloc()))
00703       return -1;
00704 
00705    args.options = data;
00706 
00707    if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options)) {
00708       destroy_jack_data(jack_data);
00709       return -1;
00710    }
00711 
00712    if (init_jack_data(chan, jack_data)) {
00713       destroy_jack_data(jack_data);
00714       return -1;
00715    }
00716 
00717    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
00718       destroy_jack_data(jack_data);
00719       return -1;
00720    }
00721 
00722    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
00723       destroy_jack_data(jack_data);
00724       return -1;
00725    }
00726 
00727    while (!jack_data->stop) {
00728       struct ast_frame *f;
00729 
00730       ast_waitfor(chan, -1);
00731 
00732       f = ast_read(chan);
00733       if (!f) {
00734          jack_data->stop = 1;
00735          continue;
00736       }
00737 
00738       switch (f->frametype) {
00739       case AST_FRAME_CONTROL:
00740          if (f->subclass == AST_CONTROL_HANGUP)
00741             jack_data->stop = 1;
00742          break;
00743       case AST_FRAME_VOICE:
00744          queue_voice_frame(jack_data, f);
00745       default:
00746          break;
00747       }
00748 
00749       ast_frfree(f);
00750 
00751       handle_jack_audio(chan, jack_data, NULL);
00752    }
00753 
00754    jack_data = destroy_jack_data(jack_data);
00755 
00756    return 0;
00757 }
00758 
00759 static void jack_hook_ds_destroy(void *data)
00760 {
00761    struct jack_data *jack_data = data;
00762 
00763    destroy_jack_data(jack_data);
00764 }
00765 
00766 static const struct ast_datastore_info jack_hook_ds_info = {
00767    .type = "JACK_HOOK",
00768    .destroy = jack_hook_ds_destroy,
00769 };
00770 
00771 static int jack_hook_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, 
00772    struct ast_frame *frame, enum ast_audiohook_direction direction)
00773 {
00774    struct ast_datastore *datastore;
00775    struct jack_data *jack_data;
00776 
00777    if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
00778       return 0;
00779 
00780    if (direction != AST_AUDIOHOOK_DIRECTION_READ)
00781       return 0;
00782 
00783    if (frame->frametype != AST_FRAME_VOICE)
00784       return 0;
00785 
00786    if (frame->subclass != AST_FORMAT_SLINEAR) {
00787       ast_log(LOG_WARNING, "Expected frame in SLINEAR for the audiohook, but got format %d\n",
00788          frame->subclass);
00789       return 0;
00790    }
00791 
00792    ast_channel_lock(chan);
00793 
00794    if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
00795       ast_log(LOG_ERROR, "JACK_HOOK datastore not found for '%s'\n", chan->name);
00796       ast_channel_unlock(chan);
00797       return -1;
00798    }
00799 
00800    jack_data = datastore->data;
00801 
00802    queue_voice_frame(jack_data, frame);
00803 
00804    handle_jack_audio(chan, jack_data, frame);
00805 
00806    ast_channel_unlock(chan);
00807 
00808    return 0;
00809 }
00810 
00811 static int enable_jack_hook(struct ast_channel *chan, char *data)
00812 {
00813    struct ast_datastore *datastore;
00814    struct jack_data *jack_data = NULL;
00815    AST_DECLARE_APP_ARGS(args,
00816       AST_APP_ARG(mode);
00817       AST_APP_ARG(options);
00818    );
00819 
00820    AST_STANDARD_APP_ARGS(args, data);
00821 
00822    ast_channel_lock(chan);
00823 
00824    if ((datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
00825       ast_log(LOG_ERROR, "JACK_HOOK already enabled for '%s'\n", chan->name);
00826       goto return_error;
00827    }
00828 
00829    if (ast_strlen_zero(args.mode) || strcasecmp(args.mode, "manipulate")) {
00830       ast_log(LOG_ERROR, "'%s' is not a supported mode.  Only manipulate is supported.\n", 
00831          S_OR(args.mode, "<none>"));
00832       goto return_error;
00833    }
00834 
00835    if (!(jack_data = jack_data_alloc()))
00836       goto return_error;
00837 
00838    if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options))
00839       goto return_error;
00840 
00841    if (init_jack_data(chan, jack_data))
00842       goto return_error;
00843 
00844    if (!(datastore = ast_channel_datastore_alloc(&jack_hook_ds_info, NULL)))
00845       goto return_error;
00846 
00847    jack_data->has_audiohook = 1;
00848    ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK");
00849    jack_data->audiohook.manipulate_callback = jack_hook_callback;
00850 
00851    datastore->data = jack_data;
00852 
00853    if (ast_audiohook_attach(chan, &jack_data->audiohook))
00854       goto return_error;
00855 
00856    if (ast_channel_datastore_add(chan, datastore))
00857       goto return_error;
00858 
00859    ast_channel_unlock(chan);
00860 
00861    return 0;
00862 
00863 return_error:
00864    ast_channel_unlock(chan);
00865 
00866    if (jack_data)
00867       destroy_jack_data(jack_data);
00868 
00869    return -1;
00870 }
00871 
00872 static int disable_jack_hook(struct ast_channel *chan)
00873 {
00874    struct ast_datastore *datastore;
00875    struct jack_data *jack_data;
00876 
00877    ast_channel_lock(chan);
00878 
00879    if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
00880       ast_channel_unlock(chan);
00881       ast_log(LOG_WARNING, "No JACK_HOOK found to disable\n");
00882       return -1;
00883    }
00884 
00885    ast_channel_datastore_remove(chan, datastore);
00886 
00887    jack_data = datastore->data;
00888    ast_audiohook_detach(&jack_data->audiohook);
00889 
00890    /* Keep the channel locked while we destroy the datastore, so that we can
00891     * ensure that all of the jack stuff is stopped just in case another frame
00892     * tries to come through the audiohook callback. */
00893    ast_channel_datastore_free(datastore);
00894 
00895    ast_channel_unlock(chan);
00896 
00897    return 0;
00898 }
00899 
00900 static int jack_hook_write(struct ast_channel *chan, const char *cmd, char *data, 
00901    const char *value)
00902 {
00903    int res;
00904 
00905    if (!strcasecmp(value, "on"))
00906       res = enable_jack_hook(chan, data);
00907    else if (!strcasecmp(value, "off"))
00908       res = disable_jack_hook(chan);
00909    else {
00910       ast_log(LOG_ERROR, "'%s' is not a valid value for JACK_HOOK()\n", value);  
00911       res = -1;
00912    }
00913 
00914    return res;
00915 }
00916 
00917 static struct ast_custom_function jack_hook_function = {
00918    .name = "JACK_HOOK",
00919    .synopsis = "Enable a jack hook on a channel",
00920    .syntax = "JACK_HOOK(<mode>,[options])",
00921    .desc =
00922    "   The JACK_HOOK allows turning on or off jack connectivity to this channel.\n"
00923    "When the JACK_HOOK is turned on, jack ports will get created that allow\n"
00924    "access to the audio stream for this channel.  The mode specifies which mode\n"
00925    "this hook should run in.  A mode must be specified when turning the JACK_HOOK.\n"
00926    "on.  However, all arguments are optional when turning it off.\n"
00927    "\n"
00928    "   Valid modes are:\n"
00929 #if 0
00930    /* XXX TODO */
00931    "    spy -        Create a read-only audio hook.  Only an output jack port will\n"
00932    "                 get created.\n"
00933    "    whisper -    Create a write-only audio hook.  Only an input jack port will\n"
00934    "                 get created.\n"
00935 #endif
00936    "    manipulate - Create a read/write audio hook.  Both an input and an output\n"
00937    "                 jack port will get created.  Audio from the channel will be\n"
00938    "                 sent out the output port and will be replaced by the audio\n"
00939    "                 coming in on the input port as it gets passed on.\n"
00940    "\n"
00941    "   Valid options are:\n"
00942    COMMON_OPTIONS
00943    "\n"
00944    " Examples:\n"
00945    "   To turn on the JACK_HOOK,\n"
00946    "     Set(JACK_HOOK(manipulate,i(pure_data_0:input0)o(pure_data_0:output0))=on)\n"
00947    "   To turn off the JACK_HOOK,\n"
00948    "     Set(JACK_HOOK()=off)\n"
00949    "",
00950    .write = jack_hook_write,
00951 };
00952 
00953 static int unload_module(void)
00954 {
00955    int res;
00956 
00957    res = ast_unregister_application(jack_app);
00958    res |= ast_custom_function_unregister(&jack_hook_function);
00959 
00960    return res;
00961 }
00962 
00963 static int load_module(void)
00964 {
00965    if (ast_register_application(jack_app, jack_exec, jack_synopsis, jack_desc))
00966       return AST_MODULE_LOAD_DECLINE;
00967 
00968    if (ast_custom_function_register(&jack_hook_function)) {
00969       ast_unregister_application(jack_app);
00970       return AST_MODULE_LOAD_DECLINE;
00971    }
00972 
00973    return AST_MODULE_LOAD_SUCCESS;
00974 }
00975 
00976 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "JACK Interface");

Generated on Thu Jul 9 13:40:18 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7