Thu Apr 28 2011 16:56:51

Asterisk developer's documentation


app_jack.c File Reference

Jack Application. More...

#include "asterisk.h"
#include <limits.h>
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#include <libresample.h>
#include "asterisk/module.h"
#include "asterisk/channel.h"
#include "asterisk/strings.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/pbx.h"
#include "asterisk/audiohook.h"
Include dependency graph for app_jack.c:

Go to the source code of this file.

Data Structures

struct  jack_data

Defines

#define COMMON_OPTIONS
 Common options between the Jack() app and JACK_HOOK() function.
#define RESAMPLE_QUALITY   1
#define RINGBUFFER_SIZE   16384

Enumerations

enum  {
  OPT_SERVER_NAME = (1 << 0), OPT_INPUT_PORT = (1 << 1), OPT_OUTPUT_PORT = (1 << 2), OPT_NOSTART_SERVER = (1 << 3),
  OPT_CLIENT_NAME = (1 << 4)
}
enum  {
  OPT_ARG_SERVER_NAME, OPT_ARG_INPUT_PORT, OPT_ARG_OUTPUT_PORT, OPT_ARG_CLIENT_NAME,
  OPT_ARG_ARRAY_SIZE
}

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int alloc_resampler (struct jack_data *jack_data, int input)
static struct jack_datadestroy_jack_data (struct jack_data *jack_data)
static int disable_jack_hook (struct ast_channel *chan)
static int enable_jack_hook (struct ast_channel *chan, char *data)
static void handle_input (void *buf, jack_nframes_t nframes, struct jack_data *jack_data)
 Handle jack input port.
static void handle_jack_audio (struct ast_channel *chan, struct jack_data *jack_data, struct ast_frame *out_frame)
 handle jack audio
static int handle_options (struct jack_data *jack_data, const char *__options_str)
static void handle_output (void *buf, jack_nframes_t nframes, struct jack_data *jack_data)
 Handle jack output port.
static int init_jack_data (struct ast_channel *chan, struct jack_data *jack_data)
static struct jack_datajack_data_alloc (void)
static int jack_exec (struct ast_channel *chan, void *data)
static int jack_hook_callback (struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
static void jack_hook_ds_destroy (void *data)
static int jack_hook_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
static int jack_process (jack_nframes_t nframes, void *arg)
static void jack_shutdown (void *arg)
static const char * jack_status_to_str (jack_status_t status)
static int load_module (void)
static void log_jack_status (const char *prefix, jack_status_t status)
static int queue_voice_frame (struct jack_data *jack_data, struct ast_frame *f)
static int unload_module (void)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "JACK Interface" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, }
static struct ast_module_infoast_module_info = &__mod_info
static char * jack_app = "JACK"
static struct ast_app_option jack_exec_options [128] = { [ 's' ] = { .flag = OPT_SERVER_NAME , .arg_index = OPT_ARG_SERVER_NAME + 1 }, [ 'i' ] = { .flag = OPT_INPUT_PORT , .arg_index = OPT_ARG_INPUT_PORT + 1 }, [ 'o' ] = { .flag = OPT_OUTPUT_PORT , .arg_index = OPT_ARG_OUTPUT_PORT + 1 }, [ 'n' ] = { .flag = OPT_NOSTART_SERVER }, [ 'c' ] = { .flag = OPT_CLIENT_NAME , .arg_index = OPT_ARG_CLIENT_NAME + 1 }, }
static struct ast_datastore_info jack_hook_ds_info
static struct ast_custom_function jack_hook_function
struct {
   jack_status_t   status
   const char *   str
jack_status_table []

Detailed Description

Jack Application.

Author:
Russell Bryant <russell@digium.com>

This is an application to connect an Asterisk channel to an input and output jack port so that the audio can be processed through another application, or to play audio from another application.

Note:
To install libresample, check it out of the following repository: $ svn co http://svn.digium.com/svn/thirdparty/libresample/trunk

Definition in file app_jack.c.


Define Documentation

#define COMMON_OPTIONS

Common options between the Jack() app and JACK_HOOK() function.

Definition at line 66 of file app_jack.c.

#define RESAMPLE_QUALITY   1

Definition at line 61 of file app_jack.c.

Referenced by alloc_resampler().

#define RINGBUFFER_SIZE   16384

Definition at line 63 of file app_jack.c.

Referenced by init_jack_data().


Enumeration Type Documentation

anonymous enum
Enumerator:
OPT_SERVER_NAME 
OPT_INPUT_PORT 
OPT_OUTPUT_PORT 
OPT_NOSTART_SERVER 
OPT_CLIENT_NAME 

Definition at line 648 of file app_jack.c.

     {
   OPT_SERVER_NAME =    (1 << 0),
   OPT_INPUT_PORT =     (1 << 1),
   OPT_OUTPUT_PORT =    (1 << 2),
   OPT_NOSTART_SERVER = (1 << 3),
   OPT_CLIENT_NAME =    (1 << 4),
};
anonymous enum
Enumerator:
OPT_ARG_SERVER_NAME 
OPT_ARG_INPUT_PORT 
OPT_ARG_OUTPUT_PORT 
OPT_ARG_CLIENT_NAME 
OPT_ARG_ARRAY_SIZE 

Definition at line 656 of file app_jack.c.


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 1025 of file app_jack.c.

static void __unreg_module ( void  ) [static]

Definition at line 1025 of file app_jack.c.

static int alloc_resampler ( struct jack_data jack_data,
int  input 
) [static]

Definition at line 188 of file app_jack.c.

References ast_log(), jack_data::client, jack_data::input_resample_factor, jack_data::input_resampler, LOG_ERROR, jack_data::output_resample_factor, jack_data::output_resampler, and RESAMPLE_QUALITY.

Referenced by jack_process(), and queue_voice_frame().

{
   double from_srate, to_srate, jack_srate;
   void **resampler;
   double *resample_factor;

   if (input && jack_data->input_resampler)
      return 0;

   if (!input && jack_data->output_resampler)
      return 0;

   jack_srate = jack_get_sample_rate(jack_data->client);

   /* XXX Hard coded 8 kHz */

   to_srate = input ? 8000.0 : jack_srate; 
   from_srate = input ? jack_srate : 8000.0;

   resample_factor = input ? &jack_data->input_resample_factor : 
      &jack_data->output_resample_factor;

   if (from_srate == to_srate) {
      /* Awesome!  The jack sample rate is the same as ours.
       * Resampling isn't needed. */
      *resample_factor = 1.0;
      return 0;
   }

   *resample_factor = to_srate / from_srate;

   resampler = input ? &jack_data->input_resampler :
      &jack_data->output_resampler;

   if (!(*resampler = resample_open(RESAMPLE_QUALITY, 
      *resample_factor, *resample_factor))) {
      ast_log(LOG_ERROR, "Failed to open %s resampler\n", 
         input ? "input" : "output");
      return -1;
   }

   return 0;
}
static struct jack_data* destroy_jack_data ( struct jack_data jack_data) [static, read]

Definition at line 340 of file app_jack.c.

References ast_audiohook_destroy(), ast_free, ast_string_field_free_memory, jack_data::audiohook, jack_data::client, jack_data::has_audiohook, jack_data::input_port, jack_data::input_rb, jack_data::input_resampler, jack_data::output_port, jack_data::output_rb, and jack_data::output_resampler.

Referenced by enable_jack_hook(), jack_exec(), and jack_hook_ds_destroy().

{
   if (jack_data->input_port) {
      jack_port_unregister(jack_data->client, jack_data->input_port);
      jack_data->input_port = NULL;
   }

   if (jack_data->output_port) {
      jack_port_unregister(jack_data->client, jack_data->output_port);
      jack_data->output_port = NULL;
   }

   if (jack_data->client) {
      jack_client_close(jack_data->client);
      jack_data->client = NULL;
   }

   if (jack_data->input_rb) {
      jack_ringbuffer_free(jack_data->input_rb);
      jack_data->input_rb = NULL;
   }

   if (jack_data->output_rb) {
      jack_ringbuffer_free(jack_data->output_rb);
      jack_data->output_rb = NULL;
   }

   if (jack_data->output_resampler) {
      resample_close(jack_data->output_resampler);
      jack_data->output_resampler = NULL;
   }
   
   if (jack_data->input_resampler) {
      resample_close(jack_data->input_resampler);
      jack_data->input_resampler = NULL;
   }

   if (jack_data->has_audiohook)
      ast_audiohook_destroy(&jack_data->audiohook);

   ast_string_field_free_memory(jack_data);

   ast_free(jack_data);

   return NULL;
}
static int disable_jack_hook ( struct ast_channel chan) [static]

Definition at line 920 of file app_jack.c.

References ast_audiohook_detach(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_datastore_free(), ast_log(), jack_data::audiohook, ast_datastore::data, and LOG_WARNING.

Referenced by jack_hook_write().

{
   struct ast_datastore *datastore;
   struct jack_data *jack_data;

   ast_channel_lock(chan);

   if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
      ast_channel_unlock(chan);
      ast_log(LOG_WARNING, "No JACK_HOOK found to disable\n");
      return -1;
   }

   ast_channel_datastore_remove(chan, datastore);

   jack_data = datastore->data;
   ast_audiohook_detach(&jack_data->audiohook);

   /* Keep the channel locked while we destroy the datastore, so that we can
    * ensure that all of the jack stuff is stopped just in case another frame
    * tries to come through the audiohook callback. */
   ast_datastore_free(datastore);

   ast_channel_unlock(chan);

   return 0;
}
static int enable_jack_hook ( struct ast_channel chan,
char *  data 
) [static]

Definition at line 859 of file app_jack.c.

References AST_APP_ARG, ast_audiohook_attach(), ast_audiohook_init(), AST_AUDIOHOOK_TYPE_MANIPULATE, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_datastore_alloc(), AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), jack_data::audiohook, ast_datastore::data, destroy_jack_data(), handle_options(), jack_data::has_audiohook, init_jack_data(), jack_data_alloc(), jack_hook_callback(), LOG_ERROR, ast_audiohook::manipulate_callback, ast_channel::name, and S_OR.

Referenced by jack_hook_write().

{
   struct ast_datastore *datastore;
   struct jack_data *jack_data = NULL;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(mode);
      AST_APP_ARG(options);
   );

   AST_STANDARD_APP_ARGS(args, data);

   ast_channel_lock(chan);

   if ((datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
      ast_log(LOG_ERROR, "JACK_HOOK already enabled for '%s'\n", chan->name);
      goto return_error;
   }

   if (ast_strlen_zero(args.mode) || strcasecmp(args.mode, "manipulate")) {
      ast_log(LOG_ERROR, "'%s' is not a supported mode.  Only manipulate is supported.\n", 
         S_OR(args.mode, "<none>"));
      goto return_error;
   }

   if (!(jack_data = jack_data_alloc()))
      goto return_error;

   if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options))
      goto return_error;

   if (init_jack_data(chan, jack_data))
      goto return_error;

   if (!(datastore = ast_datastore_alloc(&jack_hook_ds_info, NULL)))
      goto return_error;

   jack_data->has_audiohook = 1;
   ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK");
   jack_data->audiohook.manipulate_callback = jack_hook_callback;

   datastore->data = jack_data;

   if (ast_audiohook_attach(chan, &jack_data->audiohook))
      goto return_error;

   if (ast_channel_datastore_add(chan, datastore))
      goto return_error;

   ast_channel_unlock(chan);

   return 0;

return_error:
   ast_channel_unlock(chan);

   if (jack_data)
      destroy_jack_data(jack_data);

   return -1;
}
static void handle_input ( void *  buf,
jack_nframes_t  nframes,
struct jack_data jack_data 
) [static]

Handle jack input port.

Read nframes number of samples from the input buffer, resample it if necessary, and write it into the appropriate ringbuffer.

Definition at line 238 of file app_jack.c.

References ARRAY_LEN, ast_debug, ast_log(), buf, jack_data::input_rb, jack_data::input_resample_factor, jack_data::input_resampler, and LOG_ERROR.

Referenced by jack_process().

{
   short s_buf[nframes];
   float *in_buf = buf;
   size_t res;
   int i;
   size_t write_len = sizeof(s_buf);

   if (jack_data->input_resampler) {
      int total_in_buf_used = 0;
      int total_out_buf_used = 0;
      float f_buf[nframes + 1];

      memset(f_buf, 0, sizeof(f_buf));

      while (total_in_buf_used < nframes) {
         int in_buf_used;
         int out_buf_used;

         out_buf_used = resample_process(jack_data->input_resampler,
            jack_data->input_resample_factor,
            &in_buf[total_in_buf_used], nframes - total_in_buf_used,
            0, &in_buf_used,
            &f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);

         if (out_buf_used < 0)
            break;

         total_out_buf_used += out_buf_used;
         total_in_buf_used += in_buf_used;
   
         if (total_out_buf_used == ARRAY_LEN(f_buf)) {
            ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size, "
               "nframes '%d', total_out_buf_used '%d'\n", nframes, total_out_buf_used);
            break;
         }
      }

      for (i = 0; i < total_out_buf_used; i++)
         s_buf[i] = f_buf[i] * (SHRT_MAX / 1.0);
      
      write_len = total_out_buf_used * sizeof(int16_t);
   } else {
      /* No resampling needed */

      for (i = 0; i < nframes; i++)
         s_buf[i] = in_buf[i] * (SHRT_MAX / 1.0);
   }

   res = jack_ringbuffer_write(jack_data->input_rb, (const char *) s_buf, write_len);
   if (res != write_len) {
      ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
         (int) sizeof(s_buf), (int) res);
   }
}
static void handle_jack_audio ( struct ast_channel chan,
struct jack_data jack_data,
struct ast_frame out_frame 
) [static]

handle jack audio

Parameters:
[in]chanThe Asterisk channel to write the frames to if no output frame is provided.
[in]jack_dataThis is the jack_data struct that contains the input ringbuffer that audio will be read from.
[out]out_frameIf this argument is non-NULL, then assuming there is enough data avilable in the ringbuffer, the audio in this frame will get replaced with audio from the input buffer. If there is not enough data available to read at this time, then the frame data gets zeroed out.

Read data from the input ringbuffer, which is the properly resampled audio that was read from the jack input port. Write it to the channel in 20 ms frames, or fill up an output frame instead if one is provided.

Returns:
Nothing.

Definition at line 600 of file app_jack.c.

References ARRAY_LEN, ast_debug, AST_FORMAT_SLINEAR, AST_FRAME_VOICE, ast_log(), ast_write(), buf, ast_frame::data, ast_frame::datalen, ast_frame::frametype, jack_data::input_rb, LOG_ERROR, ast_frame::ptr, and ast_frame::samples.

Referenced by jack_exec(), and jack_hook_callback().

{  
   short buf[160];
   struct ast_frame f = {
      .frametype = AST_FRAME_VOICE,
      .subclass = AST_FORMAT_SLINEAR,
      .src = "JACK",
      .data.ptr = buf,
      .datalen = sizeof(buf),
      .samples = ARRAY_LEN(buf),
   };

   for (;;) {
      size_t res, read_len;
      char *read_buf;

      read_len = out_frame ? out_frame->datalen : sizeof(buf);
      read_buf = out_frame ? out_frame->data.ptr : buf;

      res = jack_ringbuffer_read_space(jack_data->input_rb);

      if (res < read_len) {
         /* Not enough data ready for another frame, move on ... */
         if (out_frame) {
            ast_debug(1, "Sending an empty frame for the JACK_HOOK\n");
            memset(out_frame->data.ptr, 0, out_frame->datalen);
         }
         break;
      }

      res = jack_ringbuffer_read(jack_data->input_rb, (char *) read_buf, read_len);

      if (res < read_len) {
         ast_log(LOG_ERROR, "Error reading from ringbuffer, even though it said there was enough data\n");
         break;
      }

      if (out_frame) {
         /* If an output frame was provided, then we just want to fill up the
          * buffer in that frame and return. */
         break;
      }

      ast_write(chan, &f);
   }
}
static int handle_options ( struct jack_data jack_data,
const char *  __options_str 
) [static]
Note:
This must be done before calling init_jack_data().

Definition at line 692 of file app_jack.c.

References ast_app_parse_options(), ast_log(), ast_strdupa, ast_string_field_set, ast_strlen_zero(), ast_test_flag, jack_exec_options, LOG_ERROR, jack_data::no_start_server, OPT_ARG_ARRAY_SIZE, OPT_ARG_CLIENT_NAME, OPT_ARG_INPUT_PORT, OPT_ARG_OUTPUT_PORT, OPT_ARG_SERVER_NAME, OPT_CLIENT_NAME, OPT_INPUT_PORT, OPT_NOSTART_SERVER, OPT_OUTPUT_PORT, OPT_SERVER_NAME, and option_args.

Referenced by enable_jack_hook(), and jack_exec().

{
   struct ast_flags options = { 0, };
   char *option_args[OPT_ARG_ARRAY_SIZE];
   char *options_str;

   options_str = ast_strdupa(__options_str);

   ast_app_parse_options(jack_exec_options, &options, option_args, options_str);

   if (ast_test_flag(&options, OPT_SERVER_NAME)) {
      if (!ast_strlen_zero(option_args[OPT_ARG_SERVER_NAME]))
         ast_string_field_set(jack_data, server_name, option_args[OPT_ARG_SERVER_NAME]);
      else {
         ast_log(LOG_ERROR, "A server name must be provided with the s() option\n");
         return -1;
      }
   }

   if (ast_test_flag(&options, OPT_CLIENT_NAME)) {
      if (!ast_strlen_zero(option_args[OPT_ARG_CLIENT_NAME]))
         ast_string_field_set(jack_data, client_name, option_args[OPT_ARG_CLIENT_NAME]);
      else {
         ast_log(LOG_ERROR, "A client name must be provided with the c() option\n");
         return -1;
      }
   }

   if (ast_test_flag(&options, OPT_INPUT_PORT)) {
      if (!ast_strlen_zero(option_args[OPT_ARG_INPUT_PORT]))
         ast_string_field_set(jack_data, connect_input_port, option_args[OPT_ARG_INPUT_PORT]);
      else {
         ast_log(LOG_ERROR, "A name must be provided with the i() option\n");
         return -1;
      }
   }

   if (ast_test_flag(&options, OPT_OUTPUT_PORT)) {
      if (!ast_strlen_zero(option_args[OPT_ARG_OUTPUT_PORT]))
         ast_string_field_set(jack_data, connect_output_port, option_args[OPT_ARG_OUTPUT_PORT]);
      else {
         ast_log(LOG_ERROR, "A name must be provided with the o() option\n");
         return -1;
      }
   }

   jack_data->no_start_server = ast_test_flag(&options, OPT_NOSTART_SERVER) ? 1 : 0;

   return 0;
}
static void handle_output ( void *  buf,
jack_nframes_t  nframes,
struct jack_data jack_data 
) [static]

Handle jack output port.

Read nframes number of samples from the ringbuffer and write it out to the output port buffer.

Definition at line 301 of file app_jack.c.

References ast_debug, len(), and jack_data::output_rb.

Referenced by jack_process().

{
   size_t res, len;

   len = nframes * sizeof(float);

   res = jack_ringbuffer_read(jack_data->output_rb, buf, len);

   if (len != res) {
      ast_debug(2, "Wanted %d bytes to send to the output port, "
         "but only got %d\n", (int) len, (int) res);
   }
}
static int init_jack_data ( struct ast_channel chan,
struct jack_data jack_data 
) [static]

Definition at line 387 of file app_jack.c.

References ast_channel_lock, ast_channel_unlock, ast_debug, ast_log(), ast_strdupa, ast_strlen_zero(), jack_data::client, jack_data::client_name, jack_data::connect_input_port, jack_data::connect_output_port, free, jack_data::input_port, jack_data::input_rb, jack_process(), jack_shutdown(), LOG_ERROR, log_jack_status(), ast_channel::name, jack_data::no_start_server, jack_data::output_port, jack_data::output_rb, RINGBUFFER_SIZE, jack_data::server_name, and status.

Referenced by enable_jack_hook(), and jack_exec().

{
   const char *client_name;
   jack_status_t status = 0;
   jack_options_t jack_options = JackNullOption;

   if (!ast_strlen_zero(jack_data->client_name)) {
      client_name = jack_data->client_name;
   } else {
      ast_channel_lock(chan);
      client_name = ast_strdupa(chan->name);
      ast_channel_unlock(chan);
   }

   if (!(jack_data->output_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
      return -1;

   if (!(jack_data->input_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
      return -1;

   if (jack_data->no_start_server)
      jack_options |= JackNoStartServer;

   if (!ast_strlen_zero(jack_data->server_name)) {
      jack_options |= JackServerName;
      jack_data->client = jack_client_open(client_name, jack_options, &status,
         jack_data->server_name);
   } else {
      jack_data->client = jack_client_open(client_name, jack_options, &status);
   }

   if (status)
      log_jack_status("Client Open Status", status);

   if (!jack_data->client)
      return -1;

   jack_data->input_port = jack_port_register(jack_data->client, "input",
      JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0);
   if (!jack_data->input_port) {
      ast_log(LOG_ERROR, "Failed to create input port for jack port\n");
      return -1;
   }

   jack_data->output_port = jack_port_register(jack_data->client, "output",
      JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0);
   if (!jack_data->output_port) {
      ast_log(LOG_ERROR, "Failed to create output port for jack port\n");
      return -1;
   }

   if (jack_set_process_callback(jack_data->client, jack_process, jack_data)) {
      ast_log(LOG_ERROR, "Failed to register process callback with jack client\n");
      return -1;
   }

   jack_on_shutdown(jack_data->client, jack_shutdown, jack_data);

   if (jack_activate(jack_data->client)) {
      ast_log(LOG_ERROR, "Unable to activate jack client\n");
      return -1;
   }

   while (!ast_strlen_zero(jack_data->connect_input_port)) {
      const char **ports;
      int i;

      ports = jack_get_ports(jack_data->client, jack_data->connect_input_port,
         NULL, JackPortIsInput);

      if (!ports) {
         ast_log(LOG_ERROR, "No input port matching '%s' was found\n",
            jack_data->connect_input_port);
         break;
      }

      for (i = 0; ports[i]; i++) {
         ast_debug(1, "Found port '%s' that matched specified input port '%s'\n",
            ports[i], jack_data->connect_input_port);
      }

      if (jack_connect(jack_data->client, jack_port_name(jack_data->output_port), ports[0])) {
         ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
            jack_port_name(jack_data->output_port));
      } else {
         ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
            jack_port_name(jack_data->output_port));
      }

      free((void *) ports);

      break;
   }

   while (!ast_strlen_zero(jack_data->connect_output_port)) {
      const char **ports;
      int i;

      ports = jack_get_ports(jack_data->client, jack_data->connect_output_port,
         NULL, JackPortIsOutput);

      if (!ports) {
         ast_log(LOG_ERROR, "No output port matching '%s' was found\n",
            jack_data->connect_output_port);
         break;
      }

      for (i = 0; ports[i]; i++) {
         ast_debug(1, "Found port '%s' that matched specified output port '%s'\n",
            ports[i], jack_data->connect_output_port);
      }

      if (jack_connect(jack_data->client, ports[0], jack_port_name(jack_data->input_port))) {
         ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
            jack_port_name(jack_data->input_port));
      } else {
         ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
            jack_port_name(jack_data->input_port));
      }

      free((void *) ports);

      break;
   }

   return 0;
}
static struct jack_data* jack_data_alloc ( void  ) [static, read]

Definition at line 674 of file app_jack.c.

References ast_calloc, ast_free, and ast_string_field_init.

Referenced by enable_jack_hook(), and jack_exec().

{
   struct jack_data *jack_data;

   if (!(jack_data = ast_calloc(1, sizeof(*jack_data))))
      return NULL;
   
   if (ast_string_field_init(jack_data, 32)) {
      ast_free(jack_data);
      return NULL;
   }

   return jack_data;
}
static int jack_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 743 of file app_jack.c.

References AST_APP_ARG, AST_CONTROL_HANGUP, AST_DECLARE_APP_ARGS, AST_FORMAT_SLINEAR, AST_FRAME_CONTROL, AST_FRAME_VOICE, ast_frfree, ast_read(), ast_set_read_format(), ast_set_write_format(), ast_strlen_zero(), ast_waitfor(), destroy_jack_data(), f, ast_frame::frametype, handle_jack_audio(), handle_options(), init_jack_data(), jack_data_alloc(), queue_voice_frame(), jack_data::stop, and ast_frame::subclass.

Referenced by load_module().

{
   struct jack_data *jack_data;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(options);
   );

   if (!(jack_data = jack_data_alloc()))
      return -1;

   args.options = data;

   if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options)) {
      destroy_jack_data(jack_data);
      return -1;
   }

   if (init_jack_data(chan, jack_data)) {
      destroy_jack_data(jack_data);
      return -1;
   }

   if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
      destroy_jack_data(jack_data);
      return -1;
   }

   if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
      destroy_jack_data(jack_data);
      return -1;
   }

   while (!jack_data->stop) {
      struct ast_frame *f;

      ast_waitfor(chan, -1);

      f = ast_read(chan);
      if (!f) {
         jack_data->stop = 1;
         continue;
      }

      switch (f->frametype) {
      case AST_FRAME_CONTROL:
         if (f->subclass == AST_CONTROL_HANGUP)
            jack_data->stop = 1;
         break;
      case AST_FRAME_VOICE:
         queue_voice_frame(jack_data, f);
      default:
         break;
      }

      ast_frfree(f);

      handle_jack_audio(chan, jack_data, NULL);
   }

   jack_data = destroy_jack_data(jack_data);

   return 0;
}
static int jack_hook_callback ( struct ast_audiohook audiohook,
struct ast_channel chan,
struct ast_frame frame,
enum ast_audiohook_direction  direction 
) [static]

Definition at line 819 of file app_jack.c.

References AST_AUDIOHOOK_DIRECTION_READ, AST_AUDIOHOOK_STATUS_DONE, ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, AST_FORMAT_SLINEAR, AST_FRAME_VOICE, ast_log(), ast_datastore::data, ast_frame::frametype, handle_jack_audio(), LOG_ERROR, LOG_WARNING, ast_channel::name, queue_voice_frame(), ast_audiohook::status, and ast_frame::subclass.

Referenced by enable_jack_hook().

{
   struct ast_datastore *datastore;
   struct jack_data *jack_data;

   if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
      return 0;

   if (direction != AST_AUDIOHOOK_DIRECTION_READ)
      return 0;

   if (frame->frametype != AST_FRAME_VOICE)
      return 0;

   if (frame->subclass != AST_FORMAT_SLINEAR) {
      ast_log(LOG_WARNING, "Expected frame in SLINEAR for the audiohook, but got format %d\n",
         frame->subclass);
      return 0;
   }

   ast_channel_lock(chan);

   if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
      ast_log(LOG_ERROR, "JACK_HOOK datastore not found for '%s'\n", chan->name);
      ast_channel_unlock(chan);
      return -1;
   }

   jack_data = datastore->data;

   queue_voice_frame(jack_data, frame);

   handle_jack_audio(chan, jack_data, frame);

   ast_channel_unlock(chan);

   return 0;
}
static void jack_hook_ds_destroy ( void *  data) [static]

Definition at line 807 of file app_jack.c.

References destroy_jack_data().

{
   struct jack_data *jack_data = data;

   destroy_jack_data(jack_data);
}
static int jack_hook_write ( struct ast_channel chan,
const char *  cmd,
char *  data,
const char *  value 
) [static]

Definition at line 948 of file app_jack.c.

References ast_log(), disable_jack_hook(), enable_jack_hook(), and LOG_ERROR.

{
   int res;

   if (!strcasecmp(value, "on"))
      res = enable_jack_hook(chan, data);
   else if (!strcasecmp(value, "off"))
      res = disable_jack_hook(chan);
   else {
      ast_log(LOG_ERROR, "'%s' is not a valid value for JACK_HOOK()\n", value);  
      res = -1;
   }

   return res;
}
static int jack_process ( jack_nframes_t  nframes,
void *  arg 
) [static]

Definition at line 316 of file app_jack.c.

References alloc_resampler(), handle_input(), handle_output(), jack_data::input_port, jack_data::input_resample_factor, and jack_data::output_port.

Referenced by init_jack_data().

{
   struct jack_data *jack_data = arg;
   void *input_port_buf, *output_port_buf;

   if (!jack_data->input_resample_factor)
      alloc_resampler(jack_data, 1);

   input_port_buf = jack_port_get_buffer(jack_data->input_port, nframes);
   handle_input(input_port_buf, nframes, jack_data);

   output_port_buf = jack_port_get_buffer(jack_data->output_port, nframes);
   handle_output(output_port_buf, nframes, jack_data);

   return 0;
}
static void jack_shutdown ( void *  arg) [static]

Definition at line 333 of file app_jack.c.

References jack_data::stop.

Referenced by init_jack_data().

{
   struct jack_data *jack_data = arg;

   jack_data->stop = 1;
}
static const char* jack_status_to_str ( jack_status_t  status) [static]

Definition at line 157 of file app_jack.c.

References ARRAY_LEN, and jack_status_table.

Referenced by log_jack_status().

{
   int i;

   for (i = 0; i < ARRAY_LEN(jack_status_table); i++) {
      if (jack_status_table[i].status == status)
         return jack_status_table[i].str;
   }

   return "Unknown Error";
}
static void log_jack_status ( const char *  prefix,
jack_status_t  status 
) [static]

Definition at line 169 of file app_jack.c.

References ast_log(), ast_str_alloca, ast_str_append(), ast_str_buffer(), ast_str_set(), first, jack_status_to_str(), LOG_NOTICE, status, and str.

Referenced by init_jack_data().

{
   struct ast_str *str = ast_str_alloca(512);
   int i, first = 0;

   for (i = 0; i < (sizeof(status) * 8); i++) {
      if (!(status & (1 << i)))
         continue;

      if (!first) {
         ast_str_set(&str, 0, "%s", jack_status_to_str((1 << i)));
         first = 1;
      } else
         ast_str_append(&str, 0, ", %s", jack_status_to_str((1 << i)));
   }
   
   ast_log(LOG_NOTICE, "%s: %s\n", prefix, ast_str_buffer(str));
}
static int queue_voice_frame ( struct jack_data jack_data,
struct ast_frame f 
) [static]

Definition at line 515 of file app_jack.c.

References alloc_resampler(), ARRAY_LEN, ast_debug, ast_log(), ast_frame::data, LOG_ERROR, jack_data::output_rb, jack_data::output_resample_factor, jack_data::output_resampler, ast_frame::ptr, and ast_frame::samples.

Referenced by jack_exec(), and jack_hook_callback().

{
   float f_buf[f->samples * 8];
   size_t f_buf_used = 0;
   int i;
   int16_t *s_buf = f->data.ptr;
   size_t res;

   memset(f_buf, 0, sizeof(f_buf));

   if (!jack_data->output_resample_factor)
      alloc_resampler(jack_data, 0);

   if (jack_data->output_resampler) {
      float in_buf[f->samples];
      int total_in_buf_used = 0;
      int total_out_buf_used = 0;

      memset(in_buf, 0, sizeof(in_buf));

      for (i = 0; i < f->samples; i++)
         in_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);

      while (total_in_buf_used < ARRAY_LEN(in_buf)) {
         int in_buf_used;
         int out_buf_used;

         out_buf_used = resample_process(jack_data->output_resampler, 
            jack_data->output_resample_factor,
            &in_buf[total_in_buf_used], ARRAY_LEN(in_buf) - total_in_buf_used, 
            0, &in_buf_used, 
            &f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);

         if (out_buf_used < 0)
            break;

         total_out_buf_used += out_buf_used;
         total_in_buf_used += in_buf_used;

         if (total_out_buf_used == ARRAY_LEN(f_buf)) {
            ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size\n");
            break;
         }
      }

      f_buf_used = total_out_buf_used;
      if (f_buf_used > ARRAY_LEN(f_buf))
         f_buf_used = ARRAY_LEN(f_buf);
   } else {
      /* No resampling needed */

      for (i = 0; i < f->samples; i++)
         f_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);

      f_buf_used = f->samples;
   }

   res = jack_ringbuffer_write(jack_data->output_rb, (const char *) f_buf, f_buf_used * sizeof(float));
   if (res != (f_buf_used * sizeof(float))) {
      ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
         (int) (f_buf_used * sizeof(float)), (int) res);
   }

   return 0;
}
static int unload_module ( void  ) [static]

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "JACK Interface" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static]

Definition at line 1025 of file app_jack.c.

Definition at line 1025 of file app_jack.c.

char* jack_app = "JACK" [static]

Definition at line 115 of file app_jack.c.

struct ast_app_option jack_exec_options[128] = { [ 's' ] = { .flag = OPT_SERVER_NAME , .arg_index = OPT_ARG_SERVER_NAME + 1 }, [ 'i' ] = { .flag = OPT_INPUT_PORT , .arg_index = OPT_ARG_INPUT_PORT + 1 }, [ 'o' ] = { .flag = OPT_OUTPUT_PORT , .arg_index = OPT_ARG_OUTPUT_PORT + 1 }, [ 'n' ] = { .flag = OPT_NOSTART_SERVER }, [ 'c' ] = { .flag = OPT_CLIENT_NAME , .arg_index = OPT_ARG_CLIENT_NAME + 1 }, } [static]

Definition at line 672 of file app_jack.c.

Referenced by handle_options().

Initial value:
 {
   .type = "JACK_HOOK",
   .destroy = jack_hook_ds_destroy,
}

Definition at line 814 of file app_jack.c.

Definition at line 965 of file app_jack.c.

struct { ... } jack_status_table[] [static]

Referenced by jack_status_to_str().