rtpsource: Modularize generation of RTP frames

Change-Id: Iad98e1753fef1927c0e8a7493372141372a38224
This commit is contained in:
Harald Welte 2020-03-15 19:38:44 +01:00
parent 7cb125d895
commit 9389188621
6 changed files with 230 additions and 7 deletions

View File

@ -4,7 +4,7 @@ OSMO_LIBS:=$(shell pkg-config --libs libosmocore libosmoctrl libosmotrau)
CFLAGS:= -g -Wall $(OSMO_CFLAGS)
LIBS:= $(OSMO_LIBS)
rtpsource: rtpsource.o ctrl_if.o
rtpsource: rtpsource.o ctrl_if.o rtp_provider.o rtp_provider_static.o
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)

View File

@ -8,6 +8,8 @@ enum {
DMAIN,
};
struct rtp_provider_instance;
struct rtp_connection {
struct llist_head list;
@ -21,6 +23,7 @@ struct rtp_connection {
uint16_t remote_port;
uint8_t rtp_pt;
struct rtp_provider_instance *rtp_prov_inst;
};
struct rtpsource_state {

54
rtpsource/rtp_provider.c Normal file
View File

@ -0,0 +1,54 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include "rtp_provider.h"
#include "internal.h"
static LLIST_HEAD(g_providers);
static LLIST_HEAD(g_prov_instances);
void rtp_provider_register(struct rtp_provider *prov)
{
llist_add_tail(&prov->list, &g_providers);
}
const struct rtp_provider *rtp_provider_find(const char *name)
{
struct rtp_provider *p;
llist_for_each_entry(p, &g_providers, list) {
if (!strcmp(name, p->name))
return p;
}
LOGP(DMAIN, LOGL_ERROR, "Couldn't find RTP provider '%s'\n", name);
return NULL;
}
struct rtp_provider_instance *
rtp_provider_instance_alloc(void *ctx, const struct rtp_provider *provider, enum codec_type codec)
{
struct rtp_provider_instance *pi;
pi = talloc_zero(ctx, struct rtp_provider_instance);
if (!pi)
return NULL;
pi->provider = provider;
pi->codec = codec;
llist_add_tail(&pi->list, &g_prov_instances);
return pi;
}
void rtp_provider_instance_free(struct rtp_provider_instance *pi)
{
llist_del(&pi->list);
talloc_free(pi);
}
int rtp_provider_instance_gen_frame(struct rtp_provider_instance *pi, uint8_t *out, size_t out_size)
{
OSMO_ASSERT(pi->provider);
return pi->provider->rtp_gen(pi, out, out_size);
}

51
rtpsource/rtp_provider.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
enum codec_type {
CODEC_ULAW,
CODEC_ALAW,
CODEC_GSM_FR,
CODEC_GSM_EFR,
CODEC_GSM_HR,
CODEC_AMR_4_75,
CODEC_AMR_5_15,
CODEC_AMR_5_90,
CODEC_AMR_6_70,
CODEC_AMR_7_40,
CODEC_AMR_7_95,
CODEC_AMR_10_2,
CODEC_AMR_12_2,
CODEC_AMR_SID,
_NUM_CODECS
};
struct rtp_provider_instance;
struct rtp_provider {
/* global list of RTP providers */
struct llist_head list;
const char *name;
/* generate the next RTP packet; return length in octests or negative on error */
int (*rtp_gen)(struct rtp_provider_instance *inst, uint8_t *out, size_t out_size);
};
struct rtp_provider_instance {
/* entry in global list of RTP provider instances */
struct llist_head list;
/* pointer to provider of which we are an instance */
const struct rtp_provider *provider;
/* codec payload we are to generate */
enum codec_type codec;
/* private user data */
void *priv;
};
void rtp_provider_register(struct rtp_provider *prov);
const struct rtp_provider *rtp_provider_find(const char *name);
struct rtp_provider_instance *rtp_provider_instance_alloc(void *ctx, const struct rtp_provider *provider, enum codec_type codec);
void rtp_provider_instance_free(struct rtp_provider_instance *pi);
int rtp_provider_instance_gen_frame(struct rtp_provider_instance *pi, uint8_t *out, size_t out_size);

View File

@ -0,0 +1,107 @@
#include <errno.h>
#include <osmocom/codec/codec.h>
#include <osmocom/core/utils.h>
#include "rtp_provider.h"
#include "internal.h"
static struct rtp_provider static_provider;
static const uint8_t len4codec[_NUM_CODECS] = {
[CODEC_ULAW] = 160,
[CODEC_ALAW] = 160,
[CODEC_GSM_FR] = GSM_FR_BYTES,
[CODEC_GSM_EFR] = GSM_EFR_BYTES,
[CODEC_GSM_HR] = GSM_HR_BYTES,
[CODEC_AMR_4_75] = 12,
[CODEC_AMR_5_15] = 13,
[CODEC_AMR_5_90] = 15,
[CODEC_AMR_6_70] = 17,
[CODEC_AMR_7_40] = 19,
[CODEC_AMR_7_95] = 20,
[CODEC_AMR_10_2] = 26,
[CODEC_AMR_12_2] = 31,
[CODEC_AMR_SID] = 5,
};
/* generate a static / fixed RTP payload of matching codec/mode */
static int rtp_gen_static(struct rtp_provider_instance *pi, uint8_t *out, size_t out_size)
{
uint8_t len;
OSMO_ASSERT(pi->provider == &static_provider);
len = len4codec[pi->codec];
if (out_size < len) {
LOGP(DMAIN, LOGL_ERROR, "out_size %zu < %u\n", out_size, len);
return -EINVAL;
}
memset(out, 0, len);
switch (pi->codec) {
case CODEC_ULAW:
case CODEC_ALAW:
break;
case CODEC_GSM_FR:
out[0] = (out[0] & 0x0f) | 0xD0; /* mask in first four bit for FR */
break;
case CODEC_GSM_EFR:
out[0] = (out[0] & 0x0f) | 0xC0; /* mask in first four bit for EFR */
break;
case CODEC_GSM_HR:
break;
case CODEC_AMR_4_75:
out[0] = 0 << 4;
out[1] = 0 << 3;
break;
case CODEC_AMR_5_15:
out[0] = 1 << 4;
out[1] = 1 << 3;
break;
case CODEC_AMR_5_90:
out[0] = 2 << 4;
out[1] = 2 << 3;
break;
case CODEC_AMR_6_70:
out[0] = 3 << 4;
out[1] = 3 << 3;
break;
case CODEC_AMR_7_40:
out[0] = 4 << 4;
out[1] = 4 << 3;
break;
case CODEC_AMR_7_95:
out[0] = 5 << 4;
out[1] = 5 << 3;
break;
case CODEC_AMR_10_2:
out[0] = 6 << 4;
out[1] = 6 << 3;
break;
case CODEC_AMR_12_2:
out[0] = 7 << 4;
out[1] = 7 << 3;
break;
case CODEC_AMR_SID:
out[0] = 2 << 4; /* CMR: 5.90 */
out[0] = 8 << 3;
break;
default:
OSMO_ASSERT(0);
}
return len;
}
static struct rtp_provider static_provider = {
.name = "static",
.rtp_gen = &rtp_gen_static,
};
static void __attribute__((constructor)) rtp_provider_static_constr(void)
{
rtp_provider_register(&static_provider);
}

View File

@ -48,6 +48,7 @@
#include <osmocom/trau/osmo_ortp.h>
#include "internal.h"
#include "rtp_provider.h"
/* find a connection based on its CNAME */
@ -64,13 +65,18 @@ struct rtp_connection *find_connection_by_cname(struct rtpsource_state *rss, con
/* create a new RTP connection for given CNAME; includes binding of local RTP port */
struct rtp_connection *create_connection(struct rtpsource_state *rss, const char *cname)
{
const struct rtp_provider *rtp_prov;
struct rtp_connection *conn;
enum codec_type codec = CODEC_GSM_FR; // TODO: configurable
const char *host;
int port;
int rc;
OSMO_ASSERT(!find_connection_by_cname(rss, cname));
rtp_prov = rtp_provider_find("static"); // TODO: configurable
OSMO_ASSERT(rtp_prov);
conn = talloc_zero(rss, struct rtp_connection);
OSMO_ASSERT(conn);
conn->cname = talloc_strdup(conn, cname);
@ -90,6 +96,9 @@ struct rtp_connection *create_connection(struct rtpsource_state *rss, const char
osmo_rtp_set_source_desc(conn->rtp_sock, conn->cname, "rtpsource", NULL, NULL,
NULL, "osmo-rtpsource", NULL);
conn->rtp_prov_inst = rtp_provider_instance_alloc(conn, rtp_prov, codec);
OSMO_ASSERT(conn->rtp_prov_inst);
llist_add_tail(&conn->list, &rss->connections);
CLOGP(conn, DMAIN, LOGL_INFO, "Created RTP connection; local=%s:%u\n",
@ -156,12 +165,11 @@ static int timerfd_cb(struct osmo_fd *ofd, unsigned int priv_nr)
/* iterate over all RTP connections and send one frame each */
llist_for_each_entry(conn, &rss->connections, list) {
int i;
/* TODO: have different sources (file+name, ...) */
uint8_t payload[33];
memset(payload, 0, sizeof(payload));
payload[0] = (payload[0] & 0x0f) | 0xD0; /* mask in first four bit for FR */
osmo_rtp_send_frame_ext(conn->rtp_sock, payload, sizeof(payload), 160, false);
uint8_t payload[256];
int i, len;
len = rtp_provider_instance_gen_frame(conn->rtp_prov_inst, payload, sizeof(payload));
osmo_rtp_send_frame_ext(conn->rtp_sock, payload, len, 160, false);
/* make sure RTP clock advances correctly, even if we missed transmit of some */
for (i = 1; i < expire_count; i++)
osmo_rtp_skipped_frame(conn->rtp_sock, 160);