mirror of https://github.com/sipwise/rtpengine.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
704 lines
18 KiB
704 lines
18 KiB
#include "cchain.h"
|
|
#ifdef HAVE_CODEC_CHAIN
|
|
#include <codec-chain/types.h>
|
|
#include <codec-chain/client.h>
|
|
#include "codeclib.h"
|
|
#include "str.h"
|
|
#include "containers.h"
|
|
#include "loglib.h"
|
|
#include <dlfcn.h>
|
|
#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
|
|
|