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

Generated on Wed Oct 28 11:50:52 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6