Sat Apr 26 2014 22:02:51

Asterisk developer's documentation


func_jitterbuffer.c File Reference

Put a jitterbuffer on the read side of a channel. More...

#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/channel.h"
#include "asterisk/framehook.h"
#include "asterisk/pbx.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/timing.h"
#include "asterisk/app.h"
Include dependency graph for func_jitterbuffer.c:

Go to the source code of this file.

Data Structures

struct  jb_framedata

Defines

#define DEFAULT_RESYNC   1000
#define DEFAULT_SIZE   200
#define DEFAULT_TARGET_EXTRA   40
#define DEFAULT_TIMER_INTERVAL   20
#define DEFAULT_TYPE   AST_JB_FIXED

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void datastore_destroy_cb (void *data)
static void hook_destroy_cb (void *framedata)
static struct ast_framehook_event_cb (struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data)
static void jb_conf_default (struct ast_jb_conf *conf)
static void jb_framedata_destroy (struct jb_framedata *framedata)
static int jb_framedata_init (struct jb_framedata *framedata, const char *data, const char *value)
static int jb_helper (struct ast_channel *chan, const char *cmd, char *data, const char *value)
static int load_module (void)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Jitter buffer for read side of channel." , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, }
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_datastore_info jb_datastore
static struct ast_custom_function jb_function

Detailed Description

Put a jitterbuffer on the read side of a channel.

Author:
David Vossel <dvossel@digium.com>

Definition in file func_jitterbuffer.c.


Define Documentation

#define DEFAULT_RESYNC   1000

Definition at line 80 of file func_jitterbuffer.c.

Referenced by jb_conf_default().

#define DEFAULT_SIZE   200

Definition at line 78 of file func_jitterbuffer.c.

Referenced by jb_conf_default().

#define DEFAULT_TARGET_EXTRA   40

Definition at line 79 of file func_jitterbuffer.c.

Referenced by jb_conf_default().

#define DEFAULT_TIMER_INTERVAL   20

Definition at line 77 of file func_jitterbuffer.c.

Referenced by jb_framedata_init().

#define DEFAULT_TYPE   AST_JB_FIXED

Definition at line 81 of file func_jitterbuffer.c.

Referenced by jb_framedata_init().


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 397 of file func_jitterbuffer.c.

static void __unreg_module ( void  ) [static]

Definition at line 397 of file func_jitterbuffer.c.

static void datastore_destroy_cb ( void *  data) [static]

Definition at line 189 of file func_jitterbuffer.c.

References ast_debug, and ast_free.

                                             {
   ast_free(data);
   ast_debug(1, "JITTERBUFFER datastore destroyed\n");
}
static void hook_destroy_cb ( void *  framedata) [static]

Definition at line 199 of file func_jitterbuffer.c.

References ast_debug, and jb_framedata_destroy().

Referenced by jb_helper().

{
   ast_debug(1, "JITTERBUFFER hook destroyed\n");
   jb_framedata_destroy((struct jb_framedata *) framedata);
}
static struct ast_frame* hook_event_cb ( struct ast_channel chan,
struct ast_frame frame,
enum ast_framehook_event  event,
void *  data 
) [static, read]

Definition at line 205 of file func_jitterbuffer.c.

References ast_channel_fdno(), ast_format_copy(), ast_format_rate(), AST_FRAME_NULL, AST_FRAME_VOICE, AST_FRAMEHOOK_EVENT_ATTACHED, AST_FRAMEHOOK_EVENT_DETACHED, AST_FRAMEHOOK_EVENT_READ, AST_FRAMEHOOK_EVENT_WRITE, ast_frdup(), AST_FRFLAG_HAS_TIMING_INFO, ast_frfree, AST_FRIENDLY_OFFSET, ast_frisolate(), AST_JB_IMPL_DROP, AST_JB_IMPL_INTERP, AST_JB_IMPL_NOFRAME, AST_JB_IMPL_OK, AST_JITTERBUFFER_FD, ast_log(), ast_null_frame, ast_samp2tv(), ast_test_flag, ast_timer_ack(), ast_timer_set_rate(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_frame::delivery, jb_framedata::first, ast_frame_subclass::format, ast_frame::frametype, ast_jb_impl::get, ast_format::id, jb_framedata::jb_impl, jb_framedata::jb_obj, jb_framedata::last_format, ast_frame::len, LOG_ERROR, ast_jb_impl::next, ast_frame::next, ast_frame::offset, ast_jb_impl::put, ast_jb_impl::put_first, ast_frame::samples, ast_frame::src, jb_framedata::start_tv, ast_frame::subclass, jb_framedata::timer, jb_framedata::timer_interval, and ast_frame::ts.

Referenced by jb_helper().

{
   struct jb_framedata *framedata = data;
   struct timeval now_tv;
   unsigned long now;
   int putframe = 0; /* signifies if audio frame was placed into the buffer or not */

   switch (event) {
   case AST_FRAMEHOOK_EVENT_READ:
      break;
   case AST_FRAMEHOOK_EVENT_ATTACHED:
   case AST_FRAMEHOOK_EVENT_DETACHED:
   case AST_FRAMEHOOK_EVENT_WRITE:
      return frame;
   }

   if (ast_channel_fdno(chan) == AST_JITTERBUFFER_FD && framedata->timer) {
      if (ast_timer_ack(framedata->timer, 1) < 0) {
         ast_log(LOG_ERROR, "Failed to acknowledge timer in jitter buffer\n");
         return frame;
      }
   }

   if (!frame) {
      return frame;
   }

   now_tv = ast_tvnow();
   now = ast_tvdiff_ms(now_tv, framedata->start_tv);

   if (frame->frametype == AST_FRAME_VOICE) {
      int res;
      struct ast_frame *jbframe;

      if (!ast_test_flag(frame, AST_FRFLAG_HAS_TIMING_INFO) || frame->len < 2 || frame->ts < 0) {
         /* only frames with timing info can enter the jitterbuffer */
         return frame;
      }

      jbframe = ast_frisolate(frame);
      ast_format_copy(&framedata->last_format, &frame->subclass.format);

      if (frame->len && (frame->len != framedata->timer_interval)) {
         framedata->timer_interval = frame->len;
         ast_timer_set_rate(framedata->timer, 1000 / framedata->timer_interval);
      }
      if (!framedata->first) {
         framedata->first = 1;
         res = framedata->jb_impl->put_first(framedata->jb_obj, jbframe, now);
      } else {
         res = framedata->jb_impl->put(framedata->jb_obj, jbframe, now);
      }
      if (res == AST_JB_IMPL_OK) {
         frame = &ast_null_frame;
      }
      putframe = 1;
   }

   if (frame->frametype == AST_FRAME_NULL) {
      int res;
      long next = framedata->jb_impl->next(framedata->jb_obj);

      /* If now is earlier than the next expected output frame
       * from the jitterbuffer we may choose to pass on retrieving
       * a frame during this read iteration.  The only exception
       * to this rule is when an audio frame is placed into the buffer
       * and the time for the next frame to come out of the buffer is
       * at least within the timer_interval of the next output frame. By
       * doing this we are able to feed off the timing of the input frames
       * and only rely on our jitterbuffer timer when frames are dropped.
       * During testing, this hybrid form of timing gave more reliable results. */
      if (now < next) {
         long int diff = next - now;
         if (!putframe) {
            return frame;
         } else if (diff >= framedata->timer_interval) {
            return frame;
         }
      }

      res = framedata->jb_impl->get(framedata->jb_obj, &frame, now, framedata->timer_interval);
      switch (res) {
      case AST_JB_IMPL_OK:
         /* got it, and pass it through */
         break;
      case AST_JB_IMPL_DROP:
         ast_frfree(frame);
         frame = &ast_null_frame;
         break;
      case AST_JB_IMPL_INTERP:
         if (framedata->last_format.id) {
            struct ast_frame tmp = { 0, };
            tmp.frametype = AST_FRAME_VOICE;
            ast_format_copy(&tmp.subclass.format, &framedata->last_format);
            /* example: 8000hz / (1000 / 20ms) = 160 samples */
            tmp.samples = ast_format_rate(&framedata->last_format) / (1000 / framedata->timer_interval);
            tmp.delivery = ast_tvadd(framedata->start_tv, ast_samp2tv(next, 1000));
            tmp.offset = AST_FRIENDLY_OFFSET;
            tmp.src  = "func_jitterbuffer interpolation";
            frame = ast_frdup(&tmp);
            break;
         }
         /* else fall through */
      case AST_JB_IMPL_NOFRAME:
         frame = &ast_null_frame;
         break;
      }
   }

   return frame;
}
static void jb_framedata_destroy ( struct jb_framedata framedata) [static]

Definition at line 95 of file func_jitterbuffer.c.

References ast_free, ast_frfree, AST_JB_IMPL_OK, ast_timer_close(), ast_jb_impl::destroy, f, jb_framedata::jb_impl, jb_framedata::jb_obj, ast_jb_impl::remove, and jb_framedata::timer.

Referenced by hook_destroy_cb(), and jb_helper().

{
   if (framedata->timer) {
      ast_timer_close(framedata->timer);
      framedata->timer = NULL;
   }
   if (framedata->jb_impl && framedata->jb_obj) {
      struct ast_frame *f;
      while (framedata->jb_impl->remove(framedata->jb_obj, &f) == AST_JB_IMPL_OK) {
         ast_frfree(f);
      }
      framedata->jb_impl->destroy(framedata->jb_obj);
      framedata->jb_obj = NULL;
   }
   ast_free(framedata);
}
static int jb_framedata_init ( struct jb_framedata framedata,
const char *  data,
const char *  value 
) [static]

Definition at line 121 of file func_jitterbuffer.c.

References args, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_JB_ADAPTIVE, AST_JB_FIXED, ast_jb_get_impl(), ast_jb_read_conf(), ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_timer_fd(), ast_timer_open(), ast_timer_set_rate(), ast_tvnow(), ast_jb_impl::create, DEFAULT_TIMER_INTERVAL, DEFAULT_TYPE, ast_jb_conf::impl, jb_framedata::jb_conf, jb_conf_default(), jb_framedata::jb_impl, jb_framedata::jb_obj, LOG_WARNING, parse(), jb_framedata::start_tv, jb_framedata::timer, jb_framedata::timer_fd, and jb_framedata::timer_interval.

Referenced by jb_helper().

{
   int jb_impl_type = DEFAULT_TYPE;

   /* Initialize defaults */
   framedata->timer_fd = -1;
   jb_conf_default(&framedata->jb_conf);
   if (!(framedata->jb_impl = ast_jb_get_impl(jb_impl_type))) {
      return -1;
   }
   if (!(framedata->timer = ast_timer_open())) {
      return -1;
   }
   framedata->timer_fd = ast_timer_fd(framedata->timer);
   framedata->timer_interval = DEFAULT_TIMER_INTERVAL;
   ast_timer_set_rate(framedata->timer, 1000 / framedata->timer_interval);
   framedata->start_tv = ast_tvnow();



   /* Now check user options to see if any of the defaults need to change. */
   if (!ast_strlen_zero(data)) {
      if (!strcasecmp(data, "fixed")) {
         jb_impl_type = AST_JB_FIXED;
      } else if (!strcasecmp(data, "adaptive")) {
         jb_impl_type = AST_JB_ADAPTIVE;
      } else {
         ast_log(LOG_WARNING, "Unknown Jitterbuffer type %s. Failed to create jitterbuffer.\n", data);
         return -1;
      }
      ast_copy_string(framedata->jb_conf.impl, data, sizeof(framedata->jb_conf.impl));
   }

   if (!ast_strlen_zero(value) && strcasecmp(value, "default")) {
      char *parse = ast_strdupa(value);
      int res = 0;
      AST_DECLARE_APP_ARGS(args,
         AST_APP_ARG(max_size);
         AST_APP_ARG(resync_threshold);
         AST_APP_ARG(target_extra);
      );

      AST_STANDARD_APP_ARGS(args, parse);
      if (!ast_strlen_zero(args.max_size)) {
         res |= ast_jb_read_conf(&framedata->jb_conf,
            "jbmaxsize",
            args.max_size);
      }
      if (!ast_strlen_zero(args.resync_threshold)) {
         res |= ast_jb_read_conf(&framedata->jb_conf,
            "jbresyncthreshold",
            args.resync_threshold);
      }
      if (!ast_strlen_zero(args.target_extra)) {
         res |= ast_jb_read_conf(&framedata->jb_conf,
            "jbtargetextra",
            args.target_extra);
      }
      if (res) {
         ast_log(LOG_WARNING, "Invalid jitterbuffer parameters %s\n", value);
      }
   }

   /* now that all the user parsing is done and nothing will change, create the jb obj */
   framedata->jb_obj = framedata->jb_impl->create(&framedata->jb_conf);
   return 0;
}
static int jb_helper ( struct ast_channel chan,
const char *  cmd,
char *  data,
const char *  value 
) [static]

Definition at line 317 of file func_jitterbuffer.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_set_fd(), ast_channel_unlock, ast_datastore_alloc(), ast_datastore_free(), ast_framehook_attach(), ast_framehook_detach(), AST_FRAMEHOOK_INTERFACE_VERSION, AST_JITTERBUFFER_FD, ast_log(), ast_datastore::data, ast_framehook_interface::data, hook_destroy_cb(), hook_event_cb(), id, jb_framedata_destroy(), jb_framedata_init(), LOG_WARNING, jb_framedata::timer_fd, and ast_framehook_interface::version.

{
   struct jb_framedata *framedata;
   struct ast_datastore *datastore = NULL;
   struct ast_framehook_interface interface = {
      .version = AST_FRAMEHOOK_INTERFACE_VERSION,
      .event_cb = hook_event_cb,
      .destroy_cb = hook_destroy_cb,
   };
   int i = 0;

   if (!chan) {
      ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
      return -1;
   }

   if (!(framedata = ast_calloc(1, sizeof(*framedata)))) {
      return 0;
   }

   if (jb_framedata_init(framedata, data, value)) {
      jb_framedata_destroy(framedata);
      return 0;
   }

   interface.data = framedata;

   ast_channel_lock(chan);
   i = ast_framehook_attach(chan, &interface);
   if (i >= 0) {
      int *id;
      if ((datastore = ast_channel_datastore_find(chan, &jb_datastore, NULL))) {
         id = datastore->data;
         ast_framehook_detach(chan, *id);
         ast_channel_datastore_remove(chan, datastore);
      }

      if (!(datastore = ast_datastore_alloc(&jb_datastore, NULL))) {
         ast_framehook_detach(chan, i);
         ast_channel_unlock(chan);
         return 0;
      }

      if (!(id = ast_calloc(1, sizeof(int)))) {
         ast_datastore_free(datastore);
         ast_framehook_detach(chan, i);
         ast_channel_unlock(chan);
         return 0;
      }

      *id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */
      datastore->data = id;
      ast_channel_datastore_add(chan, datastore);

      ast_channel_set_fd(chan, AST_JITTERBUFFER_FD, framedata->timer_fd);
   } else {
      jb_framedata_destroy(framedata);
      framedata = NULL;
   }
   ast_channel_unlock(chan);

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

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Jitter buffer for read side of channel." , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 397 of file func_jitterbuffer.c.

Definition at line 397 of file func_jitterbuffer.c.

Initial value:
 {
   .type = "jitterbuffer",
   .destroy = datastore_destroy_cb
}

Definition at line 194 of file func_jitterbuffer.c.

Initial value:
 {
   .name = "JITTERBUFFER",
   .write = jb_helper,
}

Definition at line 381 of file func_jitterbuffer.c.