Browse Source

TT#30404 initial support for adding codecs for transcoding

Change-Id: I58474ac2daee213d9f11157f61c2a3df90818fbe
changes/65/18565/9
Richard Fuchs 8 years ago
parent
commit
d510f5efe8
16 changed files with 309 additions and 153 deletions
  1. +1
    -0
      daemon/.gitignore
  2. +1
    -1
      daemon/Makefile
  3. +2
    -88
      daemon/call.c
  4. +7
    -0
      daemon/call_interfaces.c
  5. +1
    -0
      daemon/call_interfaces.h
  6. +191
    -0
      daemon/codec.c
  7. +4
    -0
      daemon/codec.h
  8. +53
    -0
      lib/codeclib.c
  9. +21
    -0
      lib/codeclib.h
  10. +13
    -0
      lib/rtplib.c
  11. +5
    -1
      lib/rtplib.h
  12. +1
    -0
      recording-daemon/.gitignore
  13. +1
    -1
      recording-daemon/Makefile
  14. +2
    -56
      recording-daemon/decoder.c
  15. +1
    -1
      recording-daemon/output.c
  16. +5
    -5
      utils/rtpengine-ng-client

+ 1
- 0
daemon/.gitignore View File

@ -7,3 +7,4 @@ core.*
auxlib.c
loglib.c
rtplib.c
codeclib.c

+ 1
- 1
daemon/Makefile View File

@ -57,7 +57,7 @@ SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_u
crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c socket.c \
media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c iptables.c tcp_listener.c \
codec.c
LIBSRCS= loglib.c auxlib.c rtplib.c
LIBSRCS= loglib.c auxlib.c rtplib.c codeclib.c
OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o)


+ 2
- 88
daemon/call.c View File

@ -1450,92 +1450,6 @@ static void __dtls_logic(const struct sdp_ng_flags *flags,
MEDIA_SET(other_media, DTLS);
}
static void __rtp_payload_type_add(struct call_media *media, struct call_media *other_media,
struct rtp_payload_type *pt)
{
struct call *call = media->call;
if (g_hash_table_lookup(media->codecs, &pt->payload_type)) {
// collision/duplicate - ignore
__payload_type_free(pt);
return;
}
/* we must duplicate the contents */
call_str_cpy(call, &pt->encoding_with_params, &pt->encoding_with_params);
call_str_cpy(call, &pt->encoding, &pt->encoding);
call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters);
call_str_cpy(call, &pt->format_parameters, &pt->format_parameters);
g_hash_table_replace(media->codecs, &pt->payload_type, pt);
GQueue *q = g_hash_table_lookup_queue_new(media->codec_names, &pt->encoding);
g_queue_push_tail(q, GUINT_TO_POINTER(pt->payload_type));
g_queue_push_tail(&media->codecs_prefs_recv, pt);
// for the other side, we need a new 'pt' struct
struct rtp_payload_type *pt_copy = g_slice_alloc(sizeof(*pt));
*pt_copy = *pt; // contents are allocated from the 'call'
g_queue_push_tail(&other_media->codecs_prefs_send, pt_copy);
// make sure we have at least an empty queue here to indicate support for this code.
// don't add anything to the queue as we don't know the reverse RTP payload type.
g_hash_table_lookup_queue_new(other_media->codec_names, &pt->encoding);
}
static void __payload_queue_free(void *qq) {
GQueue *q = qq;
g_queue_free_full(q, __payload_type_free);
}
static void __rtp_payload_types(struct call_media *media, struct call_media *other_media,
GQueue *types, GHashTable *strip,
const GQueue *offer)
{
// 'media' = receiver of this offer/answer; 'other_media' = sender of this offer/answer
struct rtp_payload_type *pt;
static const str str_all = STR_CONST_INIT("all");
GHashTable *removed = g_hash_table_new_full(str_hash, str_equal, NULL, __payload_queue_free);
int remove_all = 0;
// start fresh
g_queue_clear(&media->codecs_prefs_recv);
g_queue_clear_full(&other_media->codecs_prefs_send, __payload_type_free);
g_hash_table_remove_all(media->codecs);
g_hash_table_remove_all(media->codec_names);
if (strip && g_hash_table_lookup(strip, &str_all))
remove_all = 1;
/* we steal the entire list to avoid duplicate allocs */
while ((pt = g_queue_pop_head(types))) {
// codec stripping
if (strip) {
if (remove_all || g_hash_table_lookup(strip, &pt->encoding)) {
GQueue *q = g_hash_table_lookup_queue_new(removed, &pt->encoding);
g_queue_push_tail(q, pt);
continue;
}
}
__rtp_payload_type_add(media, other_media, pt);
}
if (offer) {
// now restore codecs that have been removed, but should be offered
for (GList *l = offer->head; l; l = l->next) {
str *codec = l->data;
GQueue *q = g_hash_table_lookup(removed, codec);
if (!q)
continue;
g_hash_table_steal(removed, codec);
for (GList *l = q->head; l; l = l->next) {
pt = l->data;
__rtp_payload_type_add(media, other_media, pt);
}
g_queue_free(q);
}
}
g_hash_table_destroy(removed);
}
static void __ice_start(struct call_media *media) {
if (MEDIA_ISSET(media, PASSTHRU)) {
@ -1658,8 +1572,8 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
}
// codec and RTP payload types handling
__rtp_payload_types(media, other_media, &sp->rtp_payload_types,
flags->codec_strip, &flags->codec_offer);
codec_rtp_payload_types(media, other_media, &sp->rtp_payload_types,
flags->codec_strip, &flags->codec_offer, &flags->codec_transcode);
codec_handlers_update(media, other_media);
/* send and recv are from our POV */


+ 7
- 0
daemon/call_interfaces.c View File

@ -618,6 +618,11 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) {
return;
if (call_ng_flags_prefix(out, s, "codec-offer-", call_ng_flags_codec_list, &out->codec_offer))
return;
if (call_ng_flags_prefix(out, s, "transcode-", call_ng_flags_codec_list, &out->codec_transcode))
return;
if (call_ng_flags_prefix(out, s, "codec-transcode-", call_ng_flags_codec_list,
&out->codec_transcode))
return;
ilog(LOG_WARN, "Unknown flag encountered: '" STR_FORMAT "'",
STR_FMT(s));
@ -693,11 +698,13 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu
/* XXX module still needs to support these */
call_ng_flags_list(out, dict, "strip", call_ng_flags_codec_ht, out->codec_strip);
call_ng_flags_list(out, dict, "offer", call_ng_flags_codec_list, &out->codec_offer);
call_ng_flags_list(out, dict, "transcode", call_ng_flags_codec_list, &out->codec_transcode);
}
}
static void call_ng_free_flags(struct sdp_ng_flags *flags) {
g_hash_table_destroy(flags->codec_strip);
g_queue_clear_full(&flags->codec_offer, str_slice_free);
g_queue_clear_full(&flags->codec_transcode, str_slice_free);
}
static const char *call_offer_answer_ng(bencode_item_t *input,


+ 1
- 0
daemon/call_interfaces.h View File

@ -33,6 +33,7 @@ struct sdp_ng_flags {
str metadata;
GHashTable *codec_strip;
GQueue codec_offer;
GQueue codec_transcode;
int asymmetric:1,
no_redis_update:1,
unidirectional:1,


+ 191
- 0
daemon/codec.c View File

@ -3,6 +3,7 @@
#include "call.h"
#include "log.h"
#include "rtplib.h"
#include "codeclib.h"
@ -128,3 +129,193 @@ void codec_packet_free(void *pp) {
p->free_func(p->s.s);
g_slice_free1(sizeof(*p), p);
}
static struct rtp_payload_type *codec_make_payload_type(const str *codec) {
const codec_def_t *dec = codec_find(codec);
if (!dec)
return NULL;
const struct rtp_payload_type *rfc_pt = rtp_get_rfc_codec(codec);
if (!rfc_pt)
return NULL; // XXX amend for other codecs
struct rtp_payload_type *ret = g_slice_alloc(sizeof(*ret));
*ret = *rfc_pt;
ret->codec_def = dec;
return ret;
}
static struct rtp_payload_type *codec_add_payload_type(const str *codec, struct call_media *media) {
struct rtp_payload_type *pt = codec_make_payload_type(codec);
if (!pt) {
ilog(LOG_WARN, "Codec '" STR_FORMAT "' requested for transcoding is not supported",
STR_FMT(codec));
return NULL;
}
// find an unused payload type number
if (pt->payload_type < 0)
pt->payload_type = 96; // default first dynamic payload type number
while (1) {
if (!g_hash_table_lookup(media->codecs, &pt->payload_type))
break; // OK
pt->payload_type++;
if (pt->payload_type < 96) // if an RFC type was taken already
pt->payload_type = 96;
else if (pt->payload_type >= 128) {
ilog(LOG_WARN, "Ran out of RTP payload type numbers while adding codec '"
STR_FORMAT "' for transcoding",
STR_FMT(codec));
__payload_type_free(pt);
return NULL;
}
}
return pt;
}
static void __rtp_payload_type_dup(struct call *call, struct rtp_payload_type *pt) {
/* we must duplicate the contents */
call_str_cpy(call, &pt->encoding_with_params, &pt->encoding_with_params);
call_str_cpy(call, &pt->encoding, &pt->encoding);
call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters);
call_str_cpy(call, &pt->format_parameters, &pt->format_parameters);
}
// consumes 'pt'
static struct rtp_payload_type *__rtp_payload_type_add_recv(struct call_media *media,
struct rtp_payload_type *pt)
{
struct rtp_payload_type *existing_pt;
if ((existing_pt = g_hash_table_lookup(media->codecs, &pt->payload_type))) {
// collision/duplicate - ignore
__payload_type_free(pt);
return existing_pt;
}
g_hash_table_replace(media->codecs, &pt->payload_type, pt);
GQueue *q = g_hash_table_lookup_queue_new(media->codec_names, &pt->encoding);
g_queue_push_tail(q, GUINT_TO_POINTER(pt->payload_type));
g_queue_push_tail(&media->codecs_prefs_recv, pt);
return pt;
}
// duplicates 'pt'
static void __rtp_payload_type_add_send(struct call_media *other_media, struct rtp_payload_type *pt) {
// for the other side, we need a new 'pt' struct
struct rtp_payload_type *pt_copy = g_slice_alloc(sizeof(*pt));
*pt_copy = *pt;
g_queue_push_tail(&other_media->codecs_prefs_send, pt_copy);
// make sure we have at least an empty queue here to indicate support for this code.
// don't add anything to the queue as we don't know the reverse RTP payload type.
g_hash_table_lookup_queue_new(other_media->codec_names, &pt->encoding);
}
// consumes 'pt'
static void __rtp_payload_type_add(struct call_media *media, struct call_media *other_media,
struct rtp_payload_type *pt)
{
// if this payload type is already present in the 'codec' table, the _recv
// function frees its argument and returns the existing entry instead.
// otherwise it returns its argument.
pt = __rtp_payload_type_add_recv(media, pt);
__rtp_payload_type_add_send(other_media, pt);
}
static void __payload_queue_free(void *qq) {
GQueue *q = qq;
g_queue_free_full(q, __payload_type_free);
}
static int __revert_codec_strip(GHashTable *removed, const str *codec,
struct call_media *media, struct call_media *other_media) {
GQueue *q = g_hash_table_lookup(removed, codec);
if (!q)
return 0;
ilog(LOG_DEBUG, "Restoring codec '" STR_FORMAT "' from stripped codecs (%u payload types)",
STR_FMT(codec), q->length);
g_hash_table_steal(removed, codec);
for (GList *l = q->head; l; l = l->next) {
struct rtp_payload_type *pt = l->data;
__rtp_payload_type_add(media, other_media, pt);
}
g_queue_free(q);
return 1;
}
void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media,
GQueue *types, GHashTable *strip,
const GQueue *offer, const GQueue *transcode)
{
// 'media' = receiver of this offer/answer; 'other_media' = sender of this offer/answer
struct call *call = media->call;
struct rtp_payload_type *pt;
static const str str_all = STR_CONST_INIT("all");
GHashTable *removed = g_hash_table_new_full(str_hash, str_equal, NULL, __payload_queue_free);
int remove_all = 0;
// start fresh
g_queue_clear(&media->codecs_prefs_recv);
g_queue_clear_full(&other_media->codecs_prefs_send, __payload_type_free);
g_hash_table_remove_all(media->codecs);
g_hash_table_remove_all(media->codec_names);
if (strip && g_hash_table_lookup(strip, &str_all))
remove_all = 1;
/* we steal the entire list to avoid duplicate allocs */
while ((pt = g_queue_pop_head(types))) {
__rtp_payload_type_dup(call, pt); // this takes care of string allocation
// codec stripping
if (strip) {
if (remove_all || g_hash_table_lookup(strip, &pt->encoding)) {
ilog(LOG_DEBUG, "Stripping codec '" STR_FORMAT "'", STR_FMT(&pt->encoding));
GQueue *q = g_hash_table_lookup_queue_new(removed, &pt->encoding);
g_queue_push_tail(q, pt);
continue;
}
}
__rtp_payload_type_add(media, other_media, pt);
}
// now restore codecs that have been removed, but should be offered
for (GList *l = offer ? offer->head : NULL; l; l = l->next) {
str *codec = l->data;
__revert_codec_strip(removed, codec, media, other_media);
}
// add transcode codecs
for (GList *l = transcode ? transcode->head : NULL; l; l = l->next) {
str *codec = l->data;
// if we wish to 'transcode' to a codec that was offered originally,
// simply restore it from the original list and handle it the same way
// as 'offer'
if (__revert_codec_strip(removed, codec, media, other_media))
continue;
// also check if maybe the codec was never stripped
if (g_hash_table_lookup(media->codec_names, codec)) {
ilog(LOG_DEBUG, "Codec '" STR_FORMAT "' requested for transcoding is already present",
STR_FMT(codec));
continue;
}
// create new payload type
pt = codec_add_payload_type(codec, media);
if (!pt)
continue;
ilog(LOG_DEBUG, "Codec '" STR_FORMAT "' added for transcoding with payload type %u",
STR_FMT(codec), pt->payload_type);
__rtp_payload_type_add_recv(media, pt);
}
g_hash_table_destroy(removed);
}

+ 4
- 0
daemon/codec.h View File

@ -32,6 +32,10 @@ void codec_handlers_free(struct call_media *);
void codec_packet_free(void *);
void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media,
GQueue *types, GHashTable *strip,
const GQueue *offer, const GQueue *transcode);


+ 53
- 0
lib/codeclib.c View File

@ -0,0 +1,53 @@
#include "codeclib.h"
#include <libavcodec/avcodec.h>
#include <glib.h>
#include "str.h"
#define CODEC_DEF_MULT_NAME(ref, id, mult, name) { \
.rtpname = #ref, \
.avcodec_id = AV_CODEC_ID_ ## id, \
.clockrate_mult = mult, \
.avcodec_name = #name, \
}
#define CODEC_DEF_MULT(ref, id, mult) CODEC_DEF_MULT_NAME(ref, id, mult, NULL)
#define CODEC_DEF_NAME(ref, id, name) CODEC_DEF_MULT_NAME(ref, id, 1, name)
#define CODEC_DEF(ref, id) CODEC_DEF_MULT(ref, id, 1)
static const struct codec_def_s codecs[] = {
CODEC_DEF(PCMA, PCM_ALAW),
CODEC_DEF(PCMU, PCM_MULAW),
CODEC_DEF(G723, G723_1),
CODEC_DEF_MULT(G722, ADPCM_G722, 2),
CODEC_DEF(QCELP, QCELP),
CODEC_DEF(G729, G729),
CODEC_DEF(speex, SPEEX),
CODEC_DEF(GSM, GSM),
CODEC_DEF(iLBC, ILBC),
CODEC_DEF_NAME(opus, OPUS, libopus),
CODEC_DEF_NAME(vorbis, VORBIS, libvorbis),
CODEC_DEF(ac3, AC3),
CODEC_DEF(eac3, EAC3),
CODEC_DEF(ATRAC3, ATRAC3),
CODEC_DEF(ATRAC-X, ATRAC3P),
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0)
CODEC_DEF(EVRC, EVRC),
CODEC_DEF(EVRC0, EVRC),
CODEC_DEF(EVRC1, EVRC),
#endif
CODEC_DEF(AMR, AMR_NB),
CODEC_DEF(AMR-WB, AMR_WB),
};
// XXX use hashtable for quicker lookup
const codec_def_t *codec_find(const str *name) {
for (int i = 0; i < G_N_ELEMENTS(codecs); i++) {
if (!str_cmp(name, codecs[i].rtpname))
return &codecs[i];
}
return NULL;
}

+ 21
- 0
lib/codeclib.h View File

@ -0,0 +1,21 @@
#ifndef __CODECLIB_H__
#define __CODECLIB_H__
#include "str.h"
struct codec_def_s {
const char *rtpname;
int clockrate_mult;
int avcodec_id;
const char *avcodec_name;
};
typedef struct codec_def_s codec_def_t;
const codec_def_t *codec_find(const str *name);
#endif

+ 13
- 0
lib/rtplib.c View File

@ -2,6 +2,7 @@
#include <arpa/inet.h>
#include "str.h"
#include "log.h"
#include "codeclib.h"
@ -125,3 +126,15 @@ const struct rtp_payload_type *rtp_get_rfc_payload_type(unsigned int type) {
return NULL;
return rtp_pt;
}
// XXX use hash table
const struct rtp_payload_type *rtp_get_rfc_codec(const str *codec) {
for (int i = 0; i < num_rfc_rtp_payload_types; i++) {
if (!rfc_rtp_payload_types[i].encoding.s)
continue;
if (str_cmp_str(codec, &rfc_rtp_payload_types[i].encoding))
continue;
return &rfc_rtp_payload_types[i];
}
return NULL;
}

+ 5
- 1
lib/rtplib.h View File

@ -3,6 +3,7 @@
#include <stdint.h>
#include "str.h"
#include "codeclib.h"
struct rtp_header {
@ -16,12 +17,14 @@ struct rtp_header {
struct rtp_payload_type {
unsigned int payload_type;
int payload_type;
str encoding_with_params;
str encoding;
unsigned int clock_rate;
str encoding_parameters;
str format_parameters;
const codec_def_t *codec_def;
};
@ -32,6 +35,7 @@ extern const int num_rfc_rtp_payload_types;
int rtp_payload(struct rtp_header **out, str *p, const str *s);
int rtp_padding(struct rtp_header *header, str *payload);
const struct rtp_payload_type *rtp_get_rfc_payload_type(unsigned int type);
const struct rtp_payload_type *rtp_get_rfc_codec(const str *codec);
#endif

+ 1
- 0
recording-daemon/.gitignore View File

@ -7,3 +7,4 @@ rtpengine-recording
auxlib.c
loglib.c
rtplib.c
codeclib.c

+ 1
- 1
recording-daemon/Makefile View File

@ -26,7 +26,7 @@ include ../lib/lib.Makefile
SRCS= epoll.c garbage.c inotify.c main.c metafile.c stream.c recaux.c packet.c \
decoder.c output.c mix.c resample.c db.c log.c forward.c
LIBSRCS= loglib.c auxlib.c rtplib.c
LIBSRCS= loglib.c auxlib.c rtplib.c codeclib.c
OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o)


+ 2
- 56
recording-daemon/decoder.c View File

@ -15,6 +15,7 @@
#include "output.h"
#include "mix.h"
#include "resample.h"
#include "codeclib.h"
struct decoder_s {
@ -33,67 +34,12 @@ struct decoder_s {
};
struct decoder_def_s {
const char *rtpname;
int clockrate_mult;
int avcodec_id;
const char *avcodec_name;
};
#define DECODER_DEF_MULT_NAME(ref, id, mult, name) { \
.rtpname = #ref, \
.avcodec_id = AV_CODEC_ID_ ## id, \
.clockrate_mult = mult, \
.avcodec_name = #name, \
}
#define DECODER_DEF_MULT(ref, id, mult) DECODER_DEF_MULT_NAME(ref, id, mult, NULL)
#define DECODER_DEF_NAME(ref, id, name) DECODER_DEF_MULT_NAME(ref, id, 1, name)
#define DECODER_DEF(ref, id) DECODER_DEF_MULT(ref, id, 1)
static const struct decoder_def_s decoders[] = {
DECODER_DEF(PCMA, PCM_ALAW),
DECODER_DEF(PCMU, PCM_MULAW),
DECODER_DEF(G723, G723_1),
DECODER_DEF_MULT(G722, ADPCM_G722, 2),
DECODER_DEF(QCELP, QCELP),
DECODER_DEF(G729, G729),
DECODER_DEF(speex, SPEEX),
DECODER_DEF(GSM, GSM),
DECODER_DEF(iLBC, ILBC),
DECODER_DEF_NAME(opus, OPUS, libopus),
DECODER_DEF_NAME(vorbis, VORBIS, libvorbis),
DECODER_DEF(ac3, AC3),
DECODER_DEF(eac3, EAC3),
DECODER_DEF(ATRAC3, ATRAC3),
DECODER_DEF(ATRAC-X, ATRAC3P),
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0)
DECODER_DEF(EVRC, EVRC),
DECODER_DEF(EVRC0, EVRC),
DECODER_DEF(EVRC1, EVRC),
#endif
DECODER_DEF(AMR, AMR_NB),
DECODER_DEF(AMR-WB, AMR_WB),
};
typedef struct decoder_def_s decoder_def_t;
int resample_audio;
static const decoder_def_t *decoder_find(const str *name) {
for (int i = 0; i < G_N_ELEMENTS(decoders); i++) {
if (!str_cmp(name, decoders[i].rtpname))
return &decoders[i];
}
return NULL;
}
decoder_t *decoder_new(const char *payload_str) {
const char *err = NULL;
@ -115,7 +61,7 @@ decoder_t *decoder_new(const char *payload_str) {
channels = 1;
}
const decoder_def_t *def = decoder_find(&name);
const codec_def_t *def = codec_find(&name);
if (!def) {
ilog(LOG_WARN, "No decoder for payload %s", payload_str);
return NULL;


+ 1
- 1
recording-daemon/output.c View File

@ -201,7 +201,7 @@ int output_config(output_t *output, const format_t *requested_format, format_t *
avcodec_parameters_from_context(output->avst->codecpar, output->avcctx);
#endif
char full_fn[PATH_MAX];
char full_fn[PATH_MAX*2];
char suff[16] = "";
for (int i = 1; i < 20; i++) {
snprintf(full_fn, sizeof(full_fn), "%s%s.%s", output->full_filename, suff, output->file_format);


+ 5
- 5
utils/rtpengine-ng-client View File

@ -47,6 +47,7 @@ GetOptions(
'media-address=s' => \$options{'media address'},
'codec-strip=s@' => \$options{'codec-strip'},
'codec-offer=s@' => \$options{'codec-offer'},
'codec-transcode=s@' => \$options{'codec-transcode'},
'flags=s@' => \$options{'flags'},
) or die;
@ -74,11 +75,10 @@ if (defined($options{direction})) {
$options{direction} =~ /(.*),(.*)/ or die;
$packet{direction} = [$1,$2];
}
if ($options{'codec-strip'} && @{$options{'codec-strip'}}) {
$packet{codec}{strip} = $options{'codec-strip'};
}
if ($options{'codec-offer'} && @{$options{'codec-offer'}}) {
$packet{codec}{offer} = $options{'codec-offer'};
for my $x (qw(strip offer transcode)) {
if ($options{'codec-'.$x} && @{$options{'codec-'.$x}}) {
$packet{codec}{$x} = $options{'codec-'.$x};
}
}
if ($options{'flags'} && @{$options{'flags'}}) {
push(@{$packet{flags}}, @{$options{'flags'}});


Loading…
Cancel
Save