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.
156 lines
4.8 KiB
156 lines
4.8 KiB
#include "call_audio.h" |
|
#include "instance_lite.h" |
|
#include "../../../lib/opus_codec.h" |
|
#include "../../../lib/debug_config.h" |
|
#include "../../../lib/mem.h" |
|
#include "../../../lib/u_async.h" |
|
#include "../../../src/call/call.h" |
|
#include <string.h> |
|
#include <pthread.h> |
|
|
|
#define CALL_AUDIO_BITRATE 32000 |
|
|
|
static pthread_mutex_t g_mtx = PTHREAD_MUTEX_INITIALIZER; |
|
static int g_active = 0; |
|
static uint64_t g_call_id = 0; |
|
static opus_codec_encoder_t* g_encoder = NULL; |
|
static opus_codec_decoder_t* g_decoder = NULL; |
|
static call_audio_pcm_recv_fn g_pcm_fn = NULL; |
|
static void* g_pcm_arg = NULL; |
|
|
|
void call_audio_set_pcm_recv_cb(call_audio_pcm_recv_fn fn, void* arg) { |
|
pthread_mutex_lock(&g_mtx); |
|
g_pcm_fn = fn; |
|
g_pcm_arg = arg; |
|
pthread_mutex_unlock(&g_mtx); |
|
} |
|
|
|
/* uasync-поток: принятый Opus-кадр пира (из call.c media_recv). */ |
|
static void call_audio_media_recv(struct UTUN_INSTANCE* inst, uint64_t call_id, |
|
const uint8_t* opus, int len, void* arg) { |
|
(void)inst; (void)arg; |
|
int16_t pcm[CALL_AUDIO_FRAME_SAMPLES]; |
|
call_audio_pcm_recv_fn fn = NULL; |
|
void* fn_arg = NULL; |
|
int n = 0; |
|
|
|
pthread_mutex_lock(&g_mtx); |
|
if (g_active && call_id == g_call_id && g_decoder && opus && len > 0) { |
|
n = opus_codec_decode(g_decoder, opus, len, pcm, CALL_AUDIO_FRAME_SAMPLES); |
|
fn = g_pcm_fn; |
|
fn_arg = g_pcm_arg; |
|
} |
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
if (n > 0 && fn) |
|
fn(call_id, pcm, n, fn_arg); |
|
} |
|
|
|
int call_audio_start(uint64_t call_id) { |
|
if (call_id == 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call_audio_start: bad call_id=0"); |
|
return -1; |
|
} |
|
|
|
pthread_mutex_lock(&g_mtx); |
|
if (g_active && g_call_id == call_id) { |
|
pthread_mutex_unlock(&g_mtx); |
|
return 0; /* уже запущен для этого звонка */ |
|
} |
|
if (g_active) { |
|
/* другой звонок активен — сбрасываем его */ |
|
pthread_mutex_unlock(&g_mtx); |
|
call_audio_stop(); |
|
pthread_mutex_lock(&g_mtx); |
|
} |
|
|
|
opus_codec_encoder_t* enc = opus_codec_encoder_create(CALL_AUDIO_SAMPLE_RATE, 1); |
|
if (!enc) { |
|
pthread_mutex_unlock(&g_mtx); |
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call_audio_start: encoder create failed"); |
|
return -1; |
|
} |
|
opus_codec_encoder_bitrate_set(enc, CALL_AUDIO_BITRATE); |
|
|
|
opus_codec_decoder_t* dec = opus_codec_decoder_create(CALL_AUDIO_SAMPLE_RATE, 1); |
|
if (!dec) { |
|
opus_codec_encoder_destroy(enc); |
|
pthread_mutex_unlock(&g_mtx); |
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call_audio_start: decoder create failed"); |
|
return -1; |
|
} |
|
|
|
g_encoder = enc; |
|
g_decoder = dec; |
|
g_call_id = call_id; |
|
g_active = 1; |
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
call_set_media_recv_cb(instance_lite_get_instance(), call_audio_media_recv, NULL); |
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call_audio_start: ok call=0x%016llx", |
|
(unsigned long long)call_id); |
|
return 0; |
|
} |
|
|
|
int call_audio_feed_pcm(uint64_t call_id, const int16_t* pcm, int count) { |
|
if (!pcm || count != CALL_AUDIO_FRAME_SAMPLES) |
|
return -1; |
|
|
|
uint8_t opus[256]; |
|
struct UASYNC* ua = NULL; |
|
struct UTUN_INSTANCE* inst = NULL; |
|
int len = -1; |
|
|
|
pthread_mutex_lock(&g_mtx); |
|
if (!g_active || call_id != g_call_id || !g_encoder) { |
|
pthread_mutex_unlock(&g_mtx); |
|
return -1; |
|
} |
|
len = opus_codec_encode(g_encoder, pcm, count, opus, (int)sizeof(opus)); |
|
ua = instance_lite_get_uasync(); |
|
inst = instance_lite_get_instance(); |
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
if (len <= 0 || !ua || !uasync_is_running(ua) || !inst) |
|
return -1; |
|
|
|
struct call_media_arg* a = (struct call_media_arg*)u_malloc(sizeof(*a)); |
|
if (!a) return -1; |
|
a->inst = inst; |
|
a->call_id = call_id; |
|
a->len = (uint16_t)len; |
|
memcpy(a->opus, opus, (size_t)len); |
|
uasync_post(ua, call_send_media_trampoline, a); |
|
return 0; |
|
} |
|
|
|
void call_audio_stop(void) { |
|
opus_codec_encoder_t* enc = NULL; |
|
opus_codec_decoder_t* dec = NULL; |
|
int was_active = 0; |
|
|
|
pthread_mutex_lock(&g_mtx); |
|
if (g_active) { |
|
was_active = 1; |
|
g_active = 0; |
|
g_call_id = 0; |
|
enc = g_encoder; g_encoder = NULL; |
|
dec = g_decoder; g_decoder = NULL; |
|
} |
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
if (!was_active) |
|
return; |
|
|
|
call_set_media_recv_cb(instance_lite_get_instance(), NULL, NULL); |
|
if (enc) opus_codec_encoder_destroy(enc); |
|
if (dec) opus_codec_decoder_destroy(dec); |
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call_audio_stop"); |
|
} |
|
|
|
int call_audio_is_active(uint64_t call_id) { |
|
pthread_mutex_lock(&g_mtx); |
|
int active = g_active && call_id == g_call_id; |
|
pthread_mutex_unlock(&g_mtx); |
|
return active; |
|
}
|
|
|