diff --git a/include/asterisk/abstract_jb.h b/include/asterisk/abstract_jb.h index 8a5e3d27ffdb30e2ac562fa47b54713f5b5288c8..173b22a5c5c9d79c313cc353c9e0c90030b48e57 100644 --- a/include/asterisk/abstract_jb.h +++ b/include/asterisk/abstract_jb.h @@ -109,6 +109,8 @@ typedef int (*jb_remove_impl)(void *jb, struct ast_frame **fout); typedef void (*jb_force_resynch_impl)(void *jb); /*! \brief Empty and reset jb */ typedef void (*jb_empty_and_reset_impl)(void *jb); +/*! \brief Check if late */ +typedef int (*jb_is_late_impl)(void *jb, long ts); /*! @@ -127,6 +129,7 @@ struct ast_jb_impl jb_remove_impl remove; jb_force_resynch_impl force_resync; jb_empty_and_reset_impl empty_and_reset; + jb_is_late_impl is_late; }; /*! diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 20f40f8634ca12e77f0b5f28d1e49de93ea1639b..108dcafe0bb62f4d821f49b74848d5c308f67b74 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -133,6 +133,8 @@ enum ast_frame_type { enum { /*! This frame contains valid timing information */ AST_FRFLAG_HAS_TIMING_INFO = (1 << 0), + /*! This frame has been requeued */ + AST_FRFLAG_REQUEUED = (1 << 1), }; struct ast_frame_subclass { diff --git a/include/jitterbuf.h b/include/jitterbuf.h index 6da11a65bd418e7a127a68d85da5008db9667a62..32579fc6c97cdd8e0f42311ab17674ad2c95e2e7 100644 --- a/include/jitterbuf.h +++ b/include/jitterbuf.h @@ -166,6 +166,9 @@ enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf); typedef void __attribute__((format(printf, 1, 2))) (*jb_output_function_t)(const char *fmt, ...); void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg); +/*! \brief Checks if the given time stamp is late */ +int jb_is_late(jitterbuf *jb, long ts); + #ifdef __cplusplus } #endif diff --git a/main/abstract_jb.c b/main/abstract_jb.c index 264ee97e620562a1eedf37c95e5b708f4c0ba268..0f0e6613fad171216ea71020bd9c349c35b6165b 100644 --- a/main/abstract_jb.c +++ b/main/abstract_jb.c @@ -65,6 +65,7 @@ static long jb_next_fixed(void *jb); static int jb_remove_fixed(void *jb, struct ast_frame **fout); static void jb_force_resynch_fixed(void *jb); static void jb_empty_and_reset_fixed(void *jb); +static int jb_is_late_fixed(void *jb, long ts); /* adaptive */ static void * jb_create_adaptive(struct ast_jb_conf *general_config); static void jb_destroy_adaptive(void *jb); @@ -75,6 +76,7 @@ static long jb_next_adaptive(void *jb); static int jb_remove_adaptive(void *jb, struct ast_frame **fout); static void jb_force_resynch_adaptive(void *jb); static void jb_empty_and_reset_adaptive(void *jb); +static int jb_is_late_adaptive(void *jb, long ts); /* Available jb implementations */ static const struct ast_jb_impl avail_impl[] = { @@ -90,6 +92,7 @@ static const struct ast_jb_impl avail_impl[] = { .remove = jb_remove_fixed, .force_resync = jb_force_resynch_fixed, .empty_and_reset = jb_empty_and_reset_fixed, + .is_late = jb_is_late_fixed, }, { .name = "adaptive", @@ -103,6 +106,7 @@ static const struct ast_jb_impl avail_impl[] = { .remove = jb_remove_adaptive, .force_resync = jb_force_resynch_adaptive, .empty_and_reset = jb_empty_and_reset_adaptive, + .is_late = jb_is_late_adaptive, } }; @@ -704,6 +708,11 @@ static void jb_empty_and_reset_fixed(void *jb) } } +static int jb_is_late_fixed(void *jb, long ts) +{ + return fixed_jb_is_late(jb, ts); +} + /* adaptive */ static void *jb_create_adaptive(struct ast_jb_conf *general_config) @@ -810,6 +819,11 @@ const struct ast_jb_impl *ast_jb_get_impl(enum ast_jb_type type) return NULL; } +static int jb_is_late_adaptive(void *jb, long ts) +{ + return jb_is_late(jb, ts); +} + #define DEFAULT_TIMER_INTERVAL 20 #define DEFAULT_SIZE 200 #define DEFAULT_TARGET_EXTRA 40 @@ -893,7 +907,22 @@ static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_fram } } - if (!frame) { + /* + * If the frame has been requeued (for instance when the translate core returns + * more than one frame) then if the frame is late we want to immediately return + * it. Otherwise attempt to insert it into the jitterbuffer. + * + * If the frame is requeued and late then in all likely hood it's a frame that + * that was previously retrieved from the jitterbuffer, passed to the translate + * core, and then put back into the channel read queue. Even if it had not been + * in the jitterbuffer prior to now it needs to be the next frame "out". + * + * However late arriving frames that have not been requeued (i.e. regular frames) + * need to be passed to the jitterbuffer so they can be appropriately dropped. As + * well any requeued frames that are not late should be put into the jitterbuffer. + */ + if (!frame || (ast_test_flag(frame, AST_FRFLAG_REQUEUED) && + framedata->jb_impl->is_late(framedata->jb_obj, frame->ts))) { return frame; } diff --git a/main/channel.c b/main/channel.c index 00cfa31aa072acc62d434d8ca4e8aba79623fff0..4f8471743e7156cbd18f81ee062e92b75ba8589c 100644 --- a/main/channel.c +++ b/main/channel.c @@ -4333,12 +4333,19 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) * at the end of the queue. */ if (AST_LIST_NEXT(f, frame_list)) { + struct ast_frame *cur, *multi_frame = AST_LIST_NEXT(f, frame_list); + + /* Mark these frames as being re-queued */ + for (cur = multi_frame; cur; cur = AST_LIST_NEXT(cur, frame_list)) { + ast_set_flag(cur, AST_FRFLAG_REQUEUED); + } + if (!readq_tail) { - ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list)); + ast_queue_frame_head(chan, multi_frame); } else { - __ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail); + __ast_queue_frame(chan, multi_frame, 0, readq_tail); } - ast_frfree(AST_LIST_NEXT(f, frame_list)); + ast_frfree(multi_frame); AST_LIST_NEXT(f, frame_list) = NULL; } diff --git a/main/fixedjitterbuf.c b/main/fixedjitterbuf.c index fc3e8cb664cb97669b0480a35186ecf782dca775..3c350b4bc45d037551f6c66b0f14ed6245ac1962 100644 --- a/main/fixedjitterbuf.c +++ b/main/fixedjitterbuf.c @@ -194,7 +194,6 @@ int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long n return fixed_jb_put(jb, data, ms, ts, now); } - int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now) { struct fixed_jb_frame *frame, *next, *newframe; @@ -347,3 +346,8 @@ int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout) return FIXED_JB_OK; } + +int fixed_jb_is_late(struct fixed_jb *jb, long ts) +{ + return jb->rxcore + jb->delay + ts < jb->next_delivery; +} diff --git a/main/fixedjitterbuf.h b/main/fixedjitterbuf.h index df9bbac5529d8c9964f98ef484f516ba836c1b36..ab8e5e2f886c9714076b5c094b112bc3da3d9a72 100644 --- a/main/fixedjitterbuf.h +++ b/main/fixedjitterbuf.h @@ -85,6 +85,9 @@ int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout); void fixed_jb_set_force_resynch(struct fixed_jb *jb); +/*! \brief Checks if the given time stamp is late */ +int fixed_jb_is_late(struct fixed_jb *jb, long ts); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/main/jitterbuf.c b/main/jitterbuf.c index 4795b6d06af8f8763e79965ef8bdf8af22cc10ea..a4d1971dddcd6dbd53a3505d933a61cac37c7e7c 100644 --- a/main/jitterbuf.c +++ b/main/jitterbuf.c @@ -843,4 +843,7 @@ enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf) return JB_OK; } - +int jb_is_late(jitterbuf *jb, long ts) +{ + return ts + jb->info.current < jb->info.next_voice_ts; +}