diff --git a/daemon/Makefile b/daemon/Makefile index 6e456b447..1294871a4 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -92,7 +92,7 @@ endif LIBSRCS := loglib.c auxlib.c rtplib.c str.c socket.c streambuf.c ssllib.c dtmflib.c mix_buffer.c poller.c \ bufferpool.c bencode.c netfilter_api.c ifeq ($(with_transcoding),yes) -LIBSRCS += codeclib.c resample.c +LIBSRCS += codeclib.c resample.c cchain.c LIBSRCS += $(CODEC_SRCS) LIBASM := mvr2s_x64_avx2.S mvr2s_x64_avx512.S mix_in_x64_avx2.S mix_in_x64_avx512bw.S mix_in_x64_sse2.S endif diff --git a/lib/cchain.c b/lib/cchain.c new file mode 100644 index 000000000..01ee34587 --- /dev/null +++ b/lib/cchain.c @@ -0,0 +1,703 @@ +#include "cchain.h" +#ifdef HAVE_CODEC_CHAIN +#include +#include +#include "codeclib.h" +#include "str.h" +#include "containers.h" +#include "loglib.h" +#include +#endif + + +#ifdef HAVE_CODEC_CHAIN + +static void *cc_lib_handle; + +static __typeof__(codec_chain_client_connect) *cc_client_connect; +static __typeof__(codec_chain_set_thread_funcs) *cc_set_thread_funcs; +static __typeof__(codec_chain_get) *cc_get; + +static __typeof__(codec_chain_client_runner_new) *cc_client_runner_new; +static __typeof__(codec_chain_client_runner_free) *cc_client_runner_free; +static __typeof__(codec_chain_client_async_runner_new) *cc_client_async_runner_new; +static __typeof__(codec_chain_client_async_runner_free) *cc_client_async_runner_free; +static __typeof__(codec_chain_runner_do) *cc_runner_do; +static __typeof__(codec_chain_async_runner_do) *cc_async_runner_do; + +static __typeof__(codec_chain_client_codec_new) *cc_client_codec_new; +static __typeof__(codec_chain_client_codec_free) *cc_client_codec_free; + +static __typeof__(*codec_chain_defs) *cc_defs; + +static codec_chain_client *cc_client; + + +static union { + codec_chain_runner *sync; + codec_chain_async_runner *async; +} cc_runners[CODEC_CHAIN_ID_MAX]; + +static struct { + unsigned int async_busy; + unsigned int async_blocked; + unsigned int async_retry; +} cc_stats[CODEC_CHAIN_ID_MAX]; + + +typedef enum { + CCC_OK, + CCC_ASYNC, + CCC_ERR, +} codec_cc_state; + +struct async_job { + str data; + unsigned long ts; + void *async_cb_obj; +}; +TYPED_GQUEUE(async_job, struct async_job); + +struct codec_cc_s { + union { + codec_chain_runner *runner; + codec_chain_async_runner *async_runner; + }; + __typeof(&cc_stats[0]) stats; + codec_chain_def *def; + codec_chain_codec *codec; + + AVPacket *avpkt; + codec_cc_state (*run)(codec_cc_t *c, const str *data, unsigned long ts, void *); + void (*clear)(void *); + void *clear_arg; + + mutex_t async_lock; + AVPacket *avpkt_async; + size_t data_len; + bool async_busy; // currently processing a packet + bool async_blocked; // couldn't find context + bool async_shutdown; // shutdown/free happened while busy + async_job_q async_jobs; + unsigned long ts; + void *(*async_init)(void *, void *, void *); + void (*async_callback)(AVPacket *, void *); + void *async_cb_obj; +}; + +static codec_cc_t *codec_cc_new_sync(codec_def_t *src, format_t *src_format, codec_def_t *dst, + format_t *dst_format, int bitrate, int ptime, + void *(*async_init)(void *, void *, void *), + void (*async_callback)(AVPacket *, void *)); +static codec_cc_t *codec_cc_new_async(codec_def_t *src, format_t *src_format, codec_def_t *dst, + format_t *dst_format, int bitrate, int ptime, + void *(*async_init)(void *, void *, void *), + void (*async_callback)(AVPacket *, void *)); + + +static bool __cc_run_async(codec_cc_t *, const str *, unsigned long, void *); + + +codec_cc_t *(*codec_cc_new)(codec_def_t *src, format_t *src_format, codec_def_t *dst, + format_t *dst_format, int bitrate, int ptime, + void *(*async_init)(void *, void *, void *), + void (*async_callback)(AVPacket *, void *)); + + + +static void cc_dlsym_resolve(const char *fn) { + cc_client_connect = dlsym_assert(cc_lib_handle, "codec_chain_client_connect", fn); + cc_set_thread_funcs = dlsym_assert(cc_lib_handle, "codec_chain_set_thread_funcs", fn); + cc_get = dlsym_assert(cc_lib_handle, "codec_chain_get", fn); + + cc_client_runner_new = dlsym_assert(cc_lib_handle, + "codec_chain_client_runner_new", fn); + + cc_client_runner_free = dlsym_assert(cc_lib_handle, + "codec_chain_client_runner_free", fn); + + cc_client_async_runner_new = dlsym_assert(cc_lib_handle, + "codec_chain_client_async_runner_new", fn); + + cc_client_async_runner_free = dlsym_assert(cc_lib_handle, + "codec_chain_client_async_runner_free", fn); + + cc_runner_do = dlsym_assert(cc_lib_handle, + "codec_chain_runner_do", fn); + + if (!rtpe_common_config_ptr->codec_chain_nonblock) { + cc_async_runner_do = dlsym_assert(cc_lib_handle, + "codec_chain_async_runner_do", fn); + } + else { + __typeof__(codec_chain_async_runner_do_nonblock) *nb = dlsym_assert(cc_lib_handle, + "codec_chain_async_runner_do_nonblock", fn); + cc_async_runner_do = nb; + } + + cc_client_codec_new = dlsym_assert(cc_lib_handle, + "codec_chain_client_codec_new", fn); + + cc_client_codec_free = dlsym_assert(cc_lib_handle, + "codec_chain_client_codec_free", fn); + + cc_defs = dlsym_assert(cc_lib_handle, + "codec_chain_defs", fn); +} + + +static codec_cc_t *codec_cc_new_dummy(codec_def_t *src, format_t *src_format, codec_def_t *dst, + format_t *dst_format, int bitrate, int ptime, + void *(*async_init)(void *, void *, void *), + void (*async_callback)(AVPacket *, void *)) +{ + return NULL; +} + +void cc_init(void) { + codec_cc_new = codec_cc_new_dummy; + + if (!rtpe_common_config_ptr->codec_chain_lib_path) + return; + + cc_lib_handle = dlopen(rtpe_common_config_ptr->codec_chain_lib_path, RTLD_NOW | RTLD_LOCAL); + if (!cc_lib_handle) + die("Failed to load libcodec-chain.so '%s': %s", + rtpe_common_config_ptr->codec_chain_lib_path, + dlerror()); + + cc_dlsym_resolve(rtpe_common_config_ptr->codec_chain_lib_path); + + cc_set_thread_funcs(codeclib_thread_init, codeclib_thread_cleanup, codeclib_thread_loop); + + cc_client = cc_client_connect(4); + if (!cc_client) + die("Failed to connect to cudecsd"); + + if (!rtpe_common_config_ptr->codec_chain_async) + codec_cc_new = codec_cc_new_sync; + else + codec_cc_new = codec_cc_new_async; + + ilog(LOG_DEBUG, "CUDA codecs initialised"); +} + +void cc_init_chain(codec_def_t *src, format_t *src_format, codec_def_t *dst, + format_t *dst_format) +{ + if (!cc_get) { + ilog(LOG_WARN, "No codec-chain support loaded"); + return; + } + + codec_chain_id id = cc_get( + (codec_chain_params) { + .name = src->rtpname, + .clock_rate = src_format->clockrate, + .channels = src_format->channels, + .ptime = 20, // XXX + }, + (codec_chain_params) { + .name = dst->rtpname, + .clock_rate = dst_format->clockrate, + .channels = dst_format->channels, + .ptime = 20, // XXX + } + ); + if (id == 0) { + ilog(LOG_WARN, "Codec chain %s -> %s not supported by library", + src->rtpname, dst->rtpname); + return; + } + if (id >= CODEC_CHAIN_ID_MAX) { + ilog(LOG_WARN, "Codec chain %s -> %s requires rebuild", + src->rtpname, dst->rtpname); + return; + } + + if (rtpe_common_config_ptr->codec_chain_async) { + if (cc_runners[id].async) + return; + cc_runners[id].async = cc_client_async_runner_new(cc_client, id, + rtpe_common_config_ptr->codec_chain_async, + rtpe_common_config_ptr->codec_chain_interval, + rtpe_common_config_ptr->codec_chain_runners, + rtpe_common_config_ptr->codec_chain_concurrency); + if (cc_runners[id].async) + ilog(LOG_DEBUG, "Created async chain runner for %s", cc_defs[id].name); + else + ilog(LOG_WARN, "Failed to create async chain runner for %s", cc_defs[id].name); + } + else { + if (cc_runners[id].sync) + return; + cc_runners[id].sync = cc_client_runner_new(cc_client, id, + rtpe_common_config_ptr->codec_chain_interval, + rtpe_common_config_ptr->codec_chain_runners, + rtpe_common_config_ptr->codec_chain_concurrency); + if (cc_runners[id].sync) + ilog(LOG_DEBUG, "Created chain runner for %s", cc_defs[id].name); + else + ilog(LOG_WARN, "Failed to create chain runner for %s", cc_defs[id].name); + } +} + +void cc_cleanup(void) { + if (!cc_lib_handle) + return; + + for (codec_chain_id id = 1; id < CODEC_CHAIN_ID_MAX; id++) { + if (!rtpe_common_config_ptr->codec_chain_async) + cc_client_runner_free(cc_client, &cc_runners[id].sync); + else + cc_client_async_runner_free(cc_client, &cc_runners[id].async); + } +} + + +static codec_cc_state cc_run(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { + AVPacket *pkt = c->avpkt; + ssize_t ret = cc_runner_do(c->runner, c->codec, + (unsigned char *) data->s, data->len, + pkt->data, pkt->size); + if (ret <= 0) + return CCC_ERR; + // XXX handle input frame sizes != 160 + + pkt->size = ret; + pkt->duration = c->def->duration(data->s, data->len); + pkt->pts = c->def->timestamp(ts, c->codec); + + return CCC_OK; +} + +static void __cc_async_job_free(struct async_job *j) { + g_free(j->data.s); + g_free(j); +} + +static void __codec_cc_free(codec_cc_t *c) { + c->clear(c->clear_arg); + while (c->async_jobs.length) { + __auto_type j = t_queue_pop_head(&c->async_jobs); + c->async_callback(NULL, j->async_cb_obj); + __cc_async_job_free(j); + } + av_packet_free(&c->avpkt); + av_packet_free(&c->avpkt_async); + g_free(c); +} + + +// lock must be held +// append job to queue +static void __cc_async_do_add_queue(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { + struct async_job *j = g_new0(__typeof__(*j), 1); + j->data = str_dup_str(data); + j->async_cb_obj = async_cb_obj; + j->ts = ts; + t_queue_push_tail(&c->async_jobs, j); +} +// check busy flag and append to queue if set +// if not busy, sets busy flag +// also check blocked flag if busy: if set, try running first job +static bool __cc_async_check_busy_blocked_queue(codec_cc_t *c, const str *data, unsigned long ts, + void *async_cb_obj, __typeof__(__cc_run_async) run_async) +{ + struct async_job *j = NULL; + async_job_q overflow = TYPED_GQUEUE_INIT; + + { + LOCK(&c->async_lock); + + if (!c->async_busy) { + // we can try running + c->async_busy = true; + return false; + } + + atomic_inc_na(&c->stats->async_busy); + + // codec is busy (either currently running or was blocked) + // append to queue + __cc_async_do_add_queue(c, data, ts, async_cb_obj); + + if (c->async_jobs.length > 20) { + ilog(LOG_WARN | LOG_FLAG_LIMIT, "Async job queue overflow (%u @ %s), dropping frames", + c->async_jobs.length, + c->def->name); + do { + __auto_type jj = t_queue_pop_head(&c->async_jobs); + t_queue_push_tail(&overflow, jj); + } while (c->async_jobs.length > 20); + } + + // if we were blocked (not currently running), try running now + if (c->async_blocked) + j = t_queue_pop_head(&c->async_jobs); + } + + while (overflow.length) { + __auto_type jj = t_queue_pop_head(&overflow); + c->async_callback(NULL, jj->async_cb_obj); + __cc_async_job_free(jj); + } + + if (j) { + atomic_inc_na(&c->stats->async_retry); + + if (!run_async(c, &j->data, j->ts, j->async_cb_obj)) { + // still blocked. return to queue + atomic_inc_na(&c->stats->async_blocked); + LOCK(&c->async_lock); + t_queue_push_head(&c->async_jobs, j); + } + else { + // unblocked, running now + __cc_async_job_free(j); + LOCK(&c->async_lock); + c->async_blocked = false; + } + } + + return true; +} +// runner failed, needed to block (no available context) +// set blocked flag and append to queue +// queue is guaranteed to be empty +static void __cc_async_blocked_queue(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { + LOCK(&c->async_lock); + __cc_async_do_add_queue(c, data, ts, async_cb_obj); + c->async_blocked = true; + // busy == true +} + +static codec_cc_state cc_X_run_async(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj, + __typeof__(__cc_run_async) run_async) +{ + if (__cc_async_check_busy_blocked_queue(c, data, ts, async_cb_obj, run_async)) + return CCC_ASYNC; + if (!run_async(c, data, ts, async_cb_obj)) + __cc_async_blocked_queue(c, data, ts, async_cb_obj); + return CCC_ASYNC; +} + +static codec_cc_state cc_run_async(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { + return cc_X_run_async(c, data, ts, async_cb_obj, __cc_run_async); +} + +static void cc_X_pkt_callback(codec_cc_t *c, ssize_t size, __typeof__(__cc_run_async) run_async) { + AVPacket *pkt = c->avpkt_async; + void *async_cb_obj = c->async_cb_obj; + c->async_cb_obj = NULL; + + c->async_callback(size >= 0 ? pkt : NULL, async_cb_obj); + + pkt->size = 0; + + struct async_job *j = NULL; + bool shutdown = false; + { + LOCK(&c->async_lock); + j = t_queue_pop_head(&c->async_jobs); + if (!j) { + if (c->async_shutdown) + shutdown = true; + else + c->async_busy = false; + } + } + + if (shutdown) { + __codec_cc_free(c); + return; + } + + if (j) { + if (!run_async(c, &j->data, j->ts, j->async_cb_obj)) { + LOCK(&c->async_lock); + t_queue_push_head(&c->async_jobs, j); + c->async_blocked = true; + } + else { + g_free(j->data.s); + g_free(j); + LOCK(&c->async_lock); + c->async_blocked = false; + } + } +} + +static void cc_run_callback(void *p, ssize_t size) { + codec_cc_t *c = p; + + AVPacket *pkt = c->avpkt_async; + + if (size >= 0) { + pkt->size = size; + pkt->duration = c->data_len * 6L; // XXX + pkt->pts = c->ts * 6L; // XXX + } + + cc_X_pkt_callback(c, size, __cc_run_async); +} + +static bool __cc_run_async(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { + AVPacket *pkt = c->avpkt_async; + pkt->size = 2048; + + c->data_len = data->len; + c->ts = ts; + c->async_cb_obj = async_cb_obj; + + return cc_async_runner_do(&c->async_runner->runner, + &c->async_runner->async, + c->codec, + (unsigned char *) data->s, data->len, + pkt->data, pkt->size, cc_run_callback, c); +} + + + +static void cc_clear(void *a) { + codec_chain_codec *c = a; + cc_client_codec_free(cc_client, &c); +} + +static codec_cc_t *codec_cc_new_sync(codec_def_t *src, format_t *src_format, codec_def_t *dst, + format_t *dst_format, int bitrate, int ptime, + void *(*async_init)(void *, void *, void *), + void (*async_callback)(AVPacket *, void *)) +{ + if (!cc_get) + return NULL; + + codec_chain_id id = cc_get( + (codec_chain_params) { + .name = src->rtpname, + .clock_rate = src_format->clockrate, + .channels = src_format->channels, + .ptime = 20, // XXX + }, + (codec_chain_params) { + .name = dst->rtpname, + .clock_rate = dst_format->clockrate, + .channels = dst_format->channels, + .ptime = 20, // XXX + } + ); + if (id == 0) + return NULL; + if (id >= CODEC_CHAIN_ID_MAX) + return NULL; + if (!cc_runners[id].sync) + return NULL; + + codec_cc_t *ret = g_new0(codec_cc_t, 1); + codec_chain_codec_args args = {0}; + ret->def = &cc_defs[id]; + if (ret->def->args == CC_ARGS_OPUS) { + args.opus = (codec_chain_opus_args) { + .bitrate = bitrate, + .complexity = rtpe_common_config_ptr->codec_chain_opus_complexity, + .application = rtpe_common_config_ptr->codec_chain_opus_application, + }; + } + ret->codec = cc_client_codec_new(cc_client, id, args); + ret->clear = cc_clear; + ret->clear_arg = ret->codec; + ret->runner = cc_runners[id].sync; + ret->stats = &cc_stats[id]; + ret->avpkt = av_packet_alloc(); + ret->run = cc_run; + + return ret; +} + +static codec_cc_t *codec_cc_new_async(codec_def_t *src, format_t *src_format, codec_def_t *dst, + format_t *dst_format, int bitrate, int ptime, + void *(*async_init)(void *, void *, void *), + void (*async_callback)(AVPacket *, void *)) +{ + if (!cc_get) + return NULL; + + codec_chain_id id = cc_get( + (codec_chain_params) { + .name = src->rtpname, + .clock_rate = src_format->clockrate, + .channels = src_format->channels, + .ptime = 20, // XXX + }, + (codec_chain_params) { + .name = dst->rtpname, + .clock_rate = dst_format->clockrate, + .channels = dst_format->channels, + .ptime = 20, // XXX + } + ); + if (id == 0) + return NULL; + if (id >= CODEC_CHAIN_ID_MAX) + return NULL; + if (!cc_runners[id].async) + return NULL; + + codec_cc_t *ret = g_new0(codec_cc_t, 1); + codec_chain_codec_args args = {0}; + ret->def = &cc_defs[id]; + if (ret->def->args == CC_ARGS_OPUS) { + args.opus = (codec_chain_opus_args) { + .bitrate = bitrate, + .complexity = rtpe_common_config_ptr->codec_chain_opus_complexity, + .application = rtpe_common_config_ptr->codec_chain_opus_application, + }; + } + ret->codec = cc_client_codec_new(cc_client, id, args); + ret->clear = cc_clear; + ret->clear_arg = ret->codec; + ret->async_runner = cc_runners[id].async; + ret->stats = &cc_stats[id]; + ret->run = cc_run_async; + ret->avpkt_async = av_packet_alloc(); + av_new_packet(ret->avpkt_async, 2048); + mutex_init(&ret->async_lock); + t_queue_init(&ret->async_jobs); + ret->async_init = async_init; + ret->async_callback = async_callback; + + return ret; +} + +void codec_cc_stop(codec_cc_t *c) { + if (!c) + return; + + // steal and fire all callbacks to release any references + + async_job_q q; + + { + LOCK(&c->async_lock); + q = c->async_jobs; + t_queue_init(&c->async_jobs); + } + + while (q.length) { + __auto_type j = t_queue_pop_head(&q); + c->async_callback(NULL, j->async_cb_obj); + __cc_async_job_free(j); + } +} + +void codec_cc_free(codec_cc_t **ccp) { + codec_cc_t *c = *ccp; + if (!c) + return; + *ccp = NULL; + + { + LOCK(&c->async_lock); + if (c->async_busy && !c->async_blocked) { + c->async_shutdown = true; + return; // wait for callback + } + } + __codec_cc_free(c); +} + + +codec_cc_stats_q codec_cc_stats(void) { + codec_cc_stats_q ret = TYPED_GQUEUE_INIT; + + for (unsigned int i = 0; i < CODEC_CHAIN_ID_MAX; i++) { + codec_chain_runner *r; + + if (rtpe_common_config_ptr->codec_chain_async) { + if (!cc_runners[i].async) + continue; + r = &cc_runners[i].async->runner; + } + else { + if (!cc_runners[i].sync) + continue; + r = cc_runners[i].sync; + } + + __auto_type q = r->queuer; + + __auto_type e = g_new0(codec_cc_stats_entry, 1); + t_queue_push_tail(&ret, e); + + g_strlcpy(e->name, r->def->name, sizeof(e->name)); + +#define LA(v) e->v = atomic_get_na(&cc_stats[i].v) + LA(async_busy); + LA(async_blocked); + LA(async_retry); +#undef LA + + for (unsigned int j = 0; j < r->num_contexts; j++) { + __auto_type c = g_new0(codec_cc_context_stats, 1); + t_queue_push_tail(&e->contexts, c); + + c->ctx_idx = j; + +#define LA(v) c->v = atomic_get_na(&q->contexts[j].v) + LA(runs); + LA(slots); + LA(run_wait); + LA(writers_wait); + LA(compute_wait); + LA(readers_wait); + + LA(run_busy); + LA(write_busy); + LA(slots_full); + LA(buf_full); + + LA(ready_wait); + LA(callbacks_preempt); + LA(callbacks_fetch); + LA(callbacks_run); + LA(loop_barrier); +#undef LA + } + } + + return ret; +} + + +AVPacket *codec_cc_input_data(codec_cc_t *c, const str *data, unsigned long ts, void *x, void *y, void *z) { + if (c->avpkt) + av_new_packet(c->avpkt, 2048); + void *async_cb_obj = NULL; + if (c->async_init) + async_cb_obj = c->async_init(x, y, z); + + codec_cc_state ret = c->run(c, data, ts, async_cb_obj); + + if (ret == CCC_ERR) { + ilog(LOG_WARN | LOG_FLAG_LIMIT, "Received error from codec-chain job"); + return c->avpkt; // return empty packet in case of error + } + if (ret == CCC_OK) + return c->avpkt; + + // CCC_ASYNC + return NULL; +} + +#else + +void cc_init(void) { } +void cc_cleanup(void) { } + + +AVPacket *codec_cc_input_data(codec_cc_t *c, const str *data, unsigned long ts, void *x, void *y, void *z) { + return NULL; +} + +#endif + diff --git a/lib/cchain.h b/lib/cchain.h new file mode 100644 index 000000000..4da8757bd --- /dev/null +++ b/lib/cchain.h @@ -0,0 +1,11 @@ +#ifndef _CCHAIN_H_ +#define _CCHAIN_H_ + +#include "codeclib.h" + +void cc_init(void); +void cc_cleanup(void); + +AVPacket *codec_cc_input_data(codec_cc_t *c, const str *data, unsigned long ts, void *x, void *y, void *z); + +#endif diff --git a/lib/codeclib.c b/lib/codeclib.c index eda02b121..3c1ff2356 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -5,10 +5,6 @@ #include #include #include -#ifdef HAVE_CODEC_CHAIN -#include -#include -#endif #include "str.h" #include "loglib.h" #include "resample.h" @@ -16,6 +12,7 @@ #include "dtmflib.h" #include "fix_frame_channel_layout.compat" #include "codecmod.h" +#include "cchain.h" @@ -35,103 +32,6 @@ static void generic_cn_dtx_cleanup(decoder_t *); static int generic_cn_dtx(decoder_t *, GQueue *, int); -static void *cc_lib_handle; - -#ifdef HAVE_CODEC_CHAIN - -static __typeof__(codec_chain_client_connect) *cc_client_connect; -static __typeof__(codec_chain_set_thread_funcs) *cc_set_thread_funcs; -static __typeof__(codec_chain_get) *cc_get; - -static __typeof__(codec_chain_client_runner_new) *cc_client_runner_new; -static __typeof__(codec_chain_client_runner_free) *cc_client_runner_free; -static __typeof__(codec_chain_client_async_runner_new) *cc_client_async_runner_new; -static __typeof__(codec_chain_client_async_runner_free) *cc_client_async_runner_free; -static __typeof__(codec_chain_runner_do) *cc_runner_do; -static __typeof__(codec_chain_async_runner_do) *cc_async_runner_do; - -static __typeof__(codec_chain_client_codec_new) *cc_client_codec_new; -static __typeof__(codec_chain_client_codec_free) *cc_client_codec_free; - -static __typeof__(*codec_chain_defs) *cc_defs; - -static codec_chain_client *cc_client; - - -static union { - codec_chain_runner *sync; - codec_chain_async_runner *async; -} cc_runners[CODEC_CHAIN_ID_MAX]; - -static struct { - unsigned int async_busy; - unsigned int async_blocked; - unsigned int async_retry; -} cc_stats[CODEC_CHAIN_ID_MAX]; - - -typedef enum { - CCC_OK, - CCC_ASYNC, - CCC_ERR, -} codec_cc_state; - -struct async_job { - str data; - unsigned long ts; - void *async_cb_obj; -}; -TYPED_GQUEUE(async_job, struct async_job); - -struct codec_cc_s { - union { - codec_chain_runner *runner; - codec_chain_async_runner *async_runner; - }; - __typeof(&cc_stats[0]) stats; - codec_chain_def *def; - codec_chain_codec *codec; - - AVPacket *avpkt; - codec_cc_state (*run)(codec_cc_t *c, const str *data, unsigned long ts, void *); - void (*clear)(void *); - void *clear_arg; - - mutex_t async_lock; - AVPacket *avpkt_async; - size_t data_len; - bool async_busy; // currently processing a packet - bool async_blocked; // couldn't find context - bool async_shutdown; // shutdown/free happened while busy - async_job_q async_jobs; - unsigned long ts; - void *(*async_init)(void *, void *, void *); - void (*async_callback)(AVPacket *, void *); - void *async_cb_obj; -}; - -static codec_cc_t *codec_cc_new_sync(codec_def_t *src, format_t *src_format, codec_def_t *dst, - format_t *dst_format, int bitrate, int ptime, - void *(*async_init)(void *, void *, void *), - void (*async_callback)(AVPacket *, void *)); -static codec_cc_t *codec_cc_new_async(codec_def_t *src, format_t *src_format, codec_def_t *dst, - format_t *dst_format, int bitrate, int ptime, - void *(*async_init)(void *, void *, void *), - void (*async_callback)(AVPacket *, void *)); - - -static bool __cc_run_async(codec_cc_t *, const str *, unsigned long, void *); - - -codec_cc_t *(*codec_cc_new)(codec_def_t *src, format_t *src_format, codec_def_t *dst, - format_t *dst_format, int bitrate, int ptime, - void *(*async_init)(void *, void *, void *), - void (*async_callback)(AVPacket *, void *)); - -#endif - - - const codec_type_t codec_type_avcodec = { .def_init = avc_def_init, @@ -642,15 +542,12 @@ void avc_def_init(struct codec_def_s *def) { } } -static void cc_cleanup(void); void codeclib_free(void) { t_hash_table_destroy(codecs_by_name_ht); t_hash_table_destroy(generic_ffmpeg_codecs); avformat_network_deinit(); cc_cleanup(); - if (cc_lib_handle) - dlclose(cc_lib_handle); } @@ -699,163 +596,6 @@ void *dlsym_assert(void *handle, const char *sym, const char *fn) { } -#ifdef HAVE_CODEC_CHAIN -static void cc_dlsym_resolve(const char *fn) { - cc_client_connect = dlsym_assert(cc_lib_handle, "codec_chain_client_connect", fn); - cc_set_thread_funcs = dlsym_assert(cc_lib_handle, "codec_chain_set_thread_funcs", fn); - cc_get = dlsym_assert(cc_lib_handle, "codec_chain_get", fn); - - cc_client_runner_new = dlsym_assert(cc_lib_handle, - "codec_chain_client_runner_new", fn); - - cc_client_runner_free = dlsym_assert(cc_lib_handle, - "codec_chain_client_runner_free", fn); - - cc_client_async_runner_new = dlsym_assert(cc_lib_handle, - "codec_chain_client_async_runner_new", fn); - - cc_client_async_runner_free = dlsym_assert(cc_lib_handle, - "codec_chain_client_async_runner_free", fn); - - cc_runner_do = dlsym_assert(cc_lib_handle, - "codec_chain_runner_do", fn); - - if (!rtpe_common_config_ptr->codec_chain_nonblock) { - cc_async_runner_do = dlsym_assert(cc_lib_handle, - "codec_chain_async_runner_do", fn); - } - else { - __typeof__(codec_chain_async_runner_do_nonblock) *nb = dlsym_assert(cc_lib_handle, - "codec_chain_async_runner_do_nonblock", fn); - cc_async_runner_do = nb; - } - - cc_client_codec_new = dlsym_assert(cc_lib_handle, - "codec_chain_client_codec_new", fn); - - cc_client_codec_free = dlsym_assert(cc_lib_handle, - "codec_chain_client_codec_free", fn); - - cc_defs = dlsym_assert(cc_lib_handle, - "codec_chain_defs", fn); -} - - -static codec_cc_t *codec_cc_new_dummy(codec_def_t *src, format_t *src_format, codec_def_t *dst, - format_t *dst_format, int bitrate, int ptime, - void *(*async_init)(void *, void *, void *), - void (*async_callback)(AVPacket *, void *)) -{ - return NULL; -} - -static void cc_init(void) { - codec_cc_new = codec_cc_new_dummy; - - if (!rtpe_common_config_ptr->codec_chain_lib_path) - return; - - cc_lib_handle = dlopen(rtpe_common_config_ptr->codec_chain_lib_path, RTLD_NOW | RTLD_LOCAL); - if (!cc_lib_handle) - die("Failed to load libcodec-chain.so '%s': %s", - rtpe_common_config_ptr->codec_chain_lib_path, - dlerror()); - - cc_dlsym_resolve(rtpe_common_config_ptr->codec_chain_lib_path); - - cc_set_thread_funcs(codeclib_thread_init, codeclib_thread_cleanup, codeclib_thread_loop); - - cc_client = cc_client_connect(4); - if (!cc_client) - die("Failed to connect to cudecsd"); - - if (!rtpe_common_config_ptr->codec_chain_async) - codec_cc_new = codec_cc_new_sync; - else - codec_cc_new = codec_cc_new_async; - - ilog(LOG_DEBUG, "CUDA codecs initialised"); -} - -void cc_init_chain(codec_def_t *src, format_t *src_format, codec_def_t *dst, - format_t *dst_format) -{ - if (!cc_get) { - ilog(LOG_WARN, "No codec-chain support loaded"); - return; - } - - codec_chain_id id = cc_get( - (codec_chain_params) { - .name = src->rtpname, - .clock_rate = src_format->clockrate, - .channels = src_format->channels, - .ptime = 20, // XXX - }, - (codec_chain_params) { - .name = dst->rtpname, - .clock_rate = dst_format->clockrate, - .channels = dst_format->channels, - .ptime = 20, // XXX - } - ); - if (id == 0) { - ilog(LOG_WARN, "Codec chain %s -> %s not supported by library", - src->rtpname, dst->rtpname); - return; - } - if (id >= CODEC_CHAIN_ID_MAX) { - ilog(LOG_WARN, "Codec chain %s -> %s requires rebuild", - src->rtpname, dst->rtpname); - return; - } - - if (rtpe_common_config_ptr->codec_chain_async) { - if (cc_runners[id].async) - return; - cc_runners[id].async = cc_client_async_runner_new(cc_client, id, - rtpe_common_config_ptr->codec_chain_async, - rtpe_common_config_ptr->codec_chain_interval, - rtpe_common_config_ptr->codec_chain_runners, - rtpe_common_config_ptr->codec_chain_concurrency); - if (cc_runners[id].async) - ilog(LOG_DEBUG, "Created async chain runner for %s", cc_defs[id].name); - else - ilog(LOG_WARN, "Failed to create async chain runner for %s", cc_defs[id].name); - } - else { - if (cc_runners[id].sync) - return; - cc_runners[id].sync = cc_client_runner_new(cc_client, id, - rtpe_common_config_ptr->codec_chain_interval, - rtpe_common_config_ptr->codec_chain_runners, - rtpe_common_config_ptr->codec_chain_concurrency); - if (cc_runners[id].sync) - ilog(LOG_DEBUG, "Created chain runner for %s", cc_defs[id].name); - else - ilog(LOG_WARN, "Failed to create chain runner for %s", cc_defs[id].name); - } -} - -static void cc_cleanup(void) { - if (!cc_lib_handle) - return; - - for (codec_chain_id id = 1; id < CODEC_CHAIN_ID_MAX; id++) { - if (!rtpe_common_config_ptr->codec_chain_async) - cc_client_runner_free(cc_client, &cc_runners[id].sync); - else - cc_client_async_runner_free(cc_client, &cc_runners[id].async); - } -} - -#else - -static void cc_init(void) { } -static void cc_cleanup(void) { } - -#endif - void codeclib_register_codec(const codec_def_t *c) { struct codec_def_s *n = realloc(__codec_defs, sizeof(*__codec_defs) * (__num_codec_defs + 1)); @@ -1684,450 +1424,6 @@ void frame_fill_dtmf_samples(enum AVSampleFormat fmt, void *samples, unsigned in -#ifdef HAVE_CODEC_CHAIN -static codec_cc_state cc_run(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { - AVPacket *pkt = c->avpkt; - ssize_t ret = cc_runner_do(c->runner, c->codec, - (unsigned char *) data->s, data->len, - pkt->data, pkt->size); - if (ret <= 0) - return CCC_ERR; - // XXX handle input frame sizes != 160 - - pkt->size = ret; - pkt->duration = c->def->duration(data->s, data->len); - pkt->pts = c->def->timestamp(ts, c->codec); - - return CCC_OK; -} - -static void __cc_async_job_free(struct async_job *j) { - g_free(j->data.s); - g_free(j); -} - -static void __codec_cc_free(codec_cc_t *c) { - c->clear(c->clear_arg); - while (c->async_jobs.length) { - __auto_type j = t_queue_pop_head(&c->async_jobs); - c->async_callback(NULL, j->async_cb_obj); - __cc_async_job_free(j); - } - av_packet_free(&c->avpkt); - av_packet_free(&c->avpkt_async); - g_free(c); -} - - -// lock must be held -// append job to queue -static void __cc_async_do_add_queue(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { - struct async_job *j = g_new0(__typeof__(*j), 1); - j->data = str_dup_str(data); - j->async_cb_obj = async_cb_obj; - j->ts = ts; - t_queue_push_tail(&c->async_jobs, j); -} -// check busy flag and append to queue if set -// if not busy, sets busy flag -// also check blocked flag if busy: if set, try running first job -static bool __cc_async_check_busy_blocked_queue(codec_cc_t *c, const str *data, unsigned long ts, - void *async_cb_obj, __typeof__(__cc_run_async) run_async) -{ - struct async_job *j = NULL; - async_job_q overflow = TYPED_GQUEUE_INIT; - - { - LOCK(&c->async_lock); - - if (!c->async_busy) { - // we can try running - c->async_busy = true; - return false; - } - - atomic_inc_na(&c->stats->async_busy); - - // codec is busy (either currently running or was blocked) - // append to queue - __cc_async_do_add_queue(c, data, ts, async_cb_obj); - - if (c->async_jobs.length > 20) { - ilog(LOG_WARN | LOG_FLAG_LIMIT, "Async job queue overflow (%u @ %s), dropping frames", - c->async_jobs.length, - c->def->name); - do { - __auto_type jj = t_queue_pop_head(&c->async_jobs); - t_queue_push_tail(&overflow, jj); - } while (c->async_jobs.length > 20); - } - - // if we were blocked (not currently running), try running now - if (c->async_blocked) - j = t_queue_pop_head(&c->async_jobs); - } - - while (overflow.length) { - __auto_type jj = t_queue_pop_head(&overflow); - c->async_callback(NULL, jj->async_cb_obj); - __cc_async_job_free(jj); - } - - if (j) { - atomic_inc_na(&c->stats->async_retry); - - if (!run_async(c, &j->data, j->ts, j->async_cb_obj)) { - // still blocked. return to queue - atomic_inc_na(&c->stats->async_blocked); - LOCK(&c->async_lock); - t_queue_push_head(&c->async_jobs, j); - } - else { - // unblocked, running now - __cc_async_job_free(j); - LOCK(&c->async_lock); - c->async_blocked = false; - } - } - - return true; -} -// runner failed, needed to block (no available context) -// set blocked flag and append to queue -// queue is guaranteed to be empty -static void __cc_async_blocked_queue(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { - LOCK(&c->async_lock); - __cc_async_do_add_queue(c, data, ts, async_cb_obj); - c->async_blocked = true; - // busy == true -} - -static codec_cc_state cc_X_run_async(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj, - __typeof__(__cc_run_async) run_async) -{ - if (__cc_async_check_busy_blocked_queue(c, data, ts, async_cb_obj, run_async)) - return CCC_ASYNC; - if (!run_async(c, data, ts, async_cb_obj)) - __cc_async_blocked_queue(c, data, ts, async_cb_obj); - return CCC_ASYNC; -} - -codec_cc_state cc_run_async(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { - return cc_X_run_async(c, data, ts, async_cb_obj, __cc_run_async); -} - -static void cc_X_pkt_callback(codec_cc_t *c, ssize_t size, __typeof__(__cc_run_async) run_async) { - AVPacket *pkt = c->avpkt_async; - void *async_cb_obj = c->async_cb_obj; - c->async_cb_obj = NULL; - - c->async_callback(size >= 0 ? pkt : NULL, async_cb_obj); - - pkt->size = 0; - - struct async_job *j = NULL; - bool shutdown = false; - { - LOCK(&c->async_lock); - j = t_queue_pop_head(&c->async_jobs); - if (!j) { - if (c->async_shutdown) - shutdown = true; - else - c->async_busy = false; - } - } - - if (shutdown) { - __codec_cc_free(c); - return; - } - - if (j) { - if (!run_async(c, &j->data, j->ts, j->async_cb_obj)) { - LOCK(&c->async_lock); - t_queue_push_head(&c->async_jobs, j); - c->async_blocked = true; - } - else { - g_free(j->data.s); - g_free(j); - LOCK(&c->async_lock); - c->async_blocked = false; - } - } -} - -static void cc_run_callback(void *p, ssize_t size) { - codec_cc_t *c = p; - - AVPacket *pkt = c->avpkt_async; - - if (size >= 0) { - pkt->size = size; - pkt->duration = c->data_len * 6L; // XXX - pkt->pts = c->ts * 6L; // XXX - } - - cc_X_pkt_callback(c, size, __cc_run_async); -} - -static bool __cc_run_async(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { - AVPacket *pkt = c->avpkt_async; - pkt->size = 2048; - - c->data_len = data->len; - c->ts = ts; - c->async_cb_obj = async_cb_obj; - - return cc_async_runner_do(&c->async_runner->runner, - &c->async_runner->async, - c->codec, - (unsigned char *) data->s, data->len, - pkt->data, pkt->size, cc_run_callback, c); -} - - - -static void cc_clear(void *a) { - codec_chain_codec *c = a; - cc_client_codec_free(cc_client, &c); -} - -static codec_cc_t *codec_cc_new_sync(codec_def_t *src, format_t *src_format, codec_def_t *dst, - format_t *dst_format, int bitrate, int ptime, - void *(*async_init)(void *, void *, void *), - void (*async_callback)(AVPacket *, void *)) -{ - if (!cc_get) - return NULL; - - codec_chain_id id = cc_get( - (codec_chain_params) { - .name = src->rtpname, - .clock_rate = src_format->clockrate, - .channels = src_format->channels, - .ptime = 20, // XXX - }, - (codec_chain_params) { - .name = dst->rtpname, - .clock_rate = dst_format->clockrate, - .channels = dst_format->channels, - .ptime = 20, // XXX - } - ); - if (id == 0) - return NULL; - if (id >= CODEC_CHAIN_ID_MAX) - return NULL; - if (!cc_runners[id].sync) - return NULL; - - codec_cc_t *ret = g_new0(codec_cc_t, 1); - codec_chain_codec_args args = {0}; - ret->def = &cc_defs[id]; - if (ret->def->args == CC_ARGS_OPUS) { - args.opus = (codec_chain_opus_args) { - .bitrate = bitrate, - .complexity = rtpe_common_config_ptr->codec_chain_opus_complexity, - .application = rtpe_common_config_ptr->codec_chain_opus_application, - }; - } - ret->codec = cc_client_codec_new(cc_client, id, args); - ret->clear = cc_clear; - ret->clear_arg = ret->codec; - ret->runner = cc_runners[id].sync; - ret->stats = &cc_stats[id]; - ret->avpkt = av_packet_alloc(); - ret->run = cc_run; - - return ret; -} - -static codec_cc_t *codec_cc_new_async(codec_def_t *src, format_t *src_format, codec_def_t *dst, - format_t *dst_format, int bitrate, int ptime, - void *(*async_init)(void *, void *, void *), - void (*async_callback)(AVPacket *, void *)) -{ - if (!cc_get) - return NULL; - - codec_chain_id id = cc_get( - (codec_chain_params) { - .name = src->rtpname, - .clock_rate = src_format->clockrate, - .channels = src_format->channels, - .ptime = 20, // XXX - }, - (codec_chain_params) { - .name = dst->rtpname, - .clock_rate = dst_format->clockrate, - .channels = dst_format->channels, - .ptime = 20, // XXX - } - ); - if (id == 0) - return NULL; - if (id >= CODEC_CHAIN_ID_MAX) - return NULL; - if (!cc_runners[id].async) - return NULL; - - codec_cc_t *ret = g_new0(codec_cc_t, 1); - codec_chain_codec_args args = {0}; - ret->def = &cc_defs[id]; - if (ret->def->args == CC_ARGS_OPUS) { - args.opus = (codec_chain_opus_args) { - .bitrate = bitrate, - .complexity = rtpe_common_config_ptr->codec_chain_opus_complexity, - .application = rtpe_common_config_ptr->codec_chain_opus_application, - }; - } - ret->codec = cc_client_codec_new(cc_client, id, args); - ret->clear = cc_clear; - ret->clear_arg = ret->codec; - ret->async_runner = cc_runners[id].async; - ret->stats = &cc_stats[id]; - ret->run = cc_run_async; - ret->avpkt_async = av_packet_alloc(); - av_new_packet(ret->avpkt_async, 2048); - mutex_init(&ret->async_lock); - t_queue_init(&ret->async_jobs); - ret->async_init = async_init; - ret->async_callback = async_callback; - - return ret; -} - -void codec_cc_stop(codec_cc_t *c) { - if (!c) - return; - - // steal and fire all callbacks to release any references - - async_job_q q; - - { - LOCK(&c->async_lock); - q = c->async_jobs; - t_queue_init(&c->async_jobs); - } - - while (q.length) { - __auto_type j = t_queue_pop_head(&q); - c->async_callback(NULL, j->async_cb_obj); - __cc_async_job_free(j); - } -} - -void codec_cc_free(codec_cc_t **ccp) { - codec_cc_t *c = *ccp; - if (!c) - return; - *ccp = NULL; - - { - LOCK(&c->async_lock); - if (c->async_busy && !c->async_blocked) { - c->async_shutdown = true; - return; // wait for callback - } - } - __codec_cc_free(c); -} - - -codec_cc_stats_q codec_cc_stats(void) { - codec_cc_stats_q ret = TYPED_GQUEUE_INIT; - - for (unsigned int i = 0; i < CODEC_CHAIN_ID_MAX; i++) { - codec_chain_runner *r; - - if (rtpe_common_config_ptr->codec_chain_async) { - if (!cc_runners[i].async) - continue; - r = &cc_runners[i].async->runner; - } - else { - if (!cc_runners[i].sync) - continue; - r = cc_runners[i].sync; - } - - __auto_type q = r->queuer; - - __auto_type e = g_new0(codec_cc_stats_entry, 1); - t_queue_push_tail(&ret, e); - - g_strlcpy(e->name, r->def->name, sizeof(e->name)); - -#define LA(v) e->v = atomic_get_na(&cc_stats[i].v) - LA(async_busy); - LA(async_blocked); - LA(async_retry); -#undef LA - - for (unsigned int j = 0; j < r->num_contexts; j++) { - __auto_type c = g_new0(codec_cc_context_stats, 1); - t_queue_push_tail(&e->contexts, c); - - c->ctx_idx = j; - -#define LA(v) c->v = atomic_get_na(&q->contexts[j].v) - LA(runs); - LA(slots); - LA(run_wait); - LA(writers_wait); - LA(compute_wait); - LA(readers_wait); - - LA(run_busy); - LA(write_busy); - LA(slots_full); - LA(buf_full); - - LA(ready_wait); - LA(callbacks_preempt); - LA(callbacks_fetch); - LA(callbacks_run); - LA(loop_barrier); -#undef LA - } - } - - return ret; -} - - -#endif - -AVPacket *codec_cc_input_data(codec_cc_t *c, const str *data, unsigned long ts, void *x, void *y, void *z) { -#ifdef HAVE_CODEC_CHAIN - if (c->avpkt) - av_new_packet(c->avpkt, 2048); - void *async_cb_obj = NULL; - if (c->async_init) - async_cb_obj = c->async_init(x, y, z); - - codec_cc_state ret = c->run(c, data, ts, async_cb_obj); - - if (ret == CCC_ERR) { - ilog(LOG_WARN | LOG_FLAG_LIMIT, "Received error from codec-chain job"); - return c->avpkt; // return empty packet in case of error - } - if (ret == CCC_OK) - return c->avpkt; - - // CCC_ASYNC - return NULL; - -#else - return NULL; -#endif -} - - - codec_def_t *codec_def_make_generic_av(enum AVCodecID id) { { RWLOCK_R(&generic_ffmpeg_codecs_lock); diff --git a/lib/codeclib.h b/lib/codeclib.h index d064c2219..c4f522df3 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -392,6 +392,8 @@ extern void (*codeclib_thread_init)(void); extern void (*codeclib_thread_cleanup)(void); extern void (*codeclib_thread_loop)(void); +void *dlsym_assert(void *handle, const char *sym, const char *fn); + void codeclib_init(int); void codeclib_free(void); diff --git a/lib/codecmod.h b/lib/codecmod.h index aa7d82df7..e0cebe5ac 100644 --- a/lib/codecmod.h +++ b/lib/codecmod.h @@ -27,8 +27,6 @@ int codeclib_set_av_opt_int(encoder_t *enc, const char *opt, int64_t val); void codeclib_key_value_parse(const str *instr, bool need_value, void (*cb)(str *key, str *value, void *data), void *data); -void *dlsym_assert(void *handle, const char *sym, const char *fn); - void codeclib_register_codec(const codec_def_t *); diff --git a/perf-tester/Makefile b/perf-tester/Makefile index 90a4bb5f3..4ac0a8fa8 100644 --- a/perf-tester/Makefile +++ b/perf-tester/Makefile @@ -46,7 +46,7 @@ LDLIBS += $(LDLIBS_CODEC_CHAIN) SRCS := main.c log.c LIBSRCS := codeclib.c loglib.c auxlib.c resample.c str.c dtmflib.c rtplib.c poller.c ssllib.c bufferpool.c \ - bencode.c uring.c + bencode.c uring.c cchain.c LIBSRCS += $(CODEC_SRCS) LIBASM := mvr2s_x64_avx2.S mvr2s_x64_avx512.S diff --git a/recording-daemon/Makefile b/recording-daemon/Makefile index 997f59906..579bcf783 100644 --- a/recording-daemon/Makefile +++ b/recording-daemon/Makefile @@ -42,7 +42,7 @@ SRCS := epoll.c garbage.c inotify.c main.c metafile.c stream.c recaux.c packet.c decoder.c output.c mix.c db.c log.c forward.c tag.c custom_poller.c notify.c tls_send.c s3.c \ gcs.c LIBSRCS := loglib.c auxlib.c rtplib.c codeclib.c resample.c str.c socket.c streambuf.c ssllib.c \ - dtmflib.c bufferpool.c bencode.c http.c s3utils.c oauth.c + dtmflib.c bufferpool.c bencode.c http.c s3utils.c oauth.c cchain.c LIBSRCS += $(CODEC_SRCS) LIBASM := mvr2s_x64_avx2.S mvr2s_x64_avx512.S mix_in_x64_avx2.S mix_in_x64_avx512bw.S mix_in_x64_sse2.S diff --git a/t/Makefile b/t/Makefile index 006cbab03..6595e968f 100644 --- a/t/Makefile +++ b/t/Makefile @@ -290,6 +290,7 @@ test-mix-buffer: test-mix-buffer.o \ ../daemon/ssrc.o \ ../lib/bencode.o \ ../lib/bufferpool.o \ + ../lib/cchain.o \ ../lib/codeclib.o \ ../lib/dtmflib.o \ ../lib/mix_buffer.o \ @@ -317,6 +318,7 @@ test-amr-decode: test-amr-decode.o \ $(COMMONOBJS) \ ../lib/amr.strhash.o \ ../lib/bencode.o \ + ../lib/cchain.o \ ../lib/codeclib.o \ ../lib/dtmflib.o \ ../lib/mvr2s_x64_avx2.o \ @@ -328,6 +330,7 @@ test-amr-encode: test-amr-encode.o \ $(COMMONOBJS) \ ../lib/amr.strhash.o \ ../lib/bencode.o \ + ../lib/cchain.o \ ../lib/codeclib.o \ ../lib/dtmflib.o \ ../lib/mvr2s_x64_avx2.o \ @@ -388,6 +391,7 @@ test-stats: test-stats.o \ ../daemon/websocket.o \ ../lib/bencode.o \ ../lib/bufferpool.o \ + ../lib/cchain.o \ ../lib/codeclib.o \ ../lib/dtmflib.o \ ../lib/mix_buffer.o \ @@ -448,6 +452,7 @@ test-transcode: test-transcode.o \ ../lib/amr.strhash.o \ ../lib/bencode.o \ ../lib/bufferpool.o \ + ../lib/cchain.o \ ../lib/codeclib.o \ ../lib/dtmflib.o \ ../lib/g711.o \ @@ -471,6 +476,7 @@ test-transcode: test-transcode.o \ test-resample: test-resample.o \ $(COMMONOBJS) \ ../lib/bencode.o \ + ../lib/cchain.o \ ../lib/codeclib.o \ ../lib/dtmflib.o \ ../lib/mvr2s_x64_avx2.o \ @@ -486,6 +492,7 @@ test-payload-tracker: test-payload-tracker.o \ ../daemon/ssrc.o \ ../lib/bencode.o \ ../lib/bufferpool.o \ + ../lib/cchain.o \ ../lib/codeclib.o \ ../lib/dtmflib.o \ ../lib/mvr2s_x64_avx2.o \