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.
332 lines
9.6 KiB
332 lines
9.6 KiB
/**
|
|
* @file rtpfmt.c
|
|
*/
|
|
/*****************************************************************************
|
|
* Copyright (C) 2001-2005 VLC authors and VideoLAN
|
|
* Copyright © 2007-2009 Rémi Denis-Courmont
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_aout.h> /* aout_FormatPrepare() */
|
|
|
|
#include "rtp.h"
|
|
#include "sdp.h"
|
|
|
|
/*
|
|
* Generic packet handlers
|
|
*/
|
|
|
|
static void codec_destroy(struct vlc_rtp_pt *pt, void *data)
|
|
{
|
|
(void) pt;
|
|
vlc_rtp_es_destroy(data);
|
|
}
|
|
|
|
/* Send a packet to ES */
|
|
static void codec_decode(struct vlc_rtp_pt *pt, void *data, block_t *block,
|
|
const struct vlc_rtp_pktinfo *restrict info)
|
|
{
|
|
(void) pt; (void) info;
|
|
block->i_dts = VLC_TICK_INVALID;
|
|
vlc_rtp_es_send(data, block);
|
|
}
|
|
|
|
/*
|
|
* Static payload types handler
|
|
*/
|
|
|
|
/* PT=3
|
|
* GSM
|
|
*/
|
|
static void *gsm_init(struct vlc_rtp_pt *pt)
|
|
{
|
|
es_format_t fmt;
|
|
|
|
es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_GSM);
|
|
fmt.audio.i_rate = 8000;
|
|
fmt.audio.i_physical_channels = AOUT_CHAN_CENTER;
|
|
return vlc_rtp_pt_request_es(pt, &fmt);
|
|
}
|
|
|
|
static const struct vlc_rtp_pt_operations rtp_audio_gsm = {
|
|
NULL, gsm_init, codec_destroy, codec_decode,
|
|
};
|
|
|
|
/* PT=12
|
|
* QCELP
|
|
*/
|
|
static void *qcelp_init(struct vlc_rtp_pt *pt)
|
|
{
|
|
es_format_t fmt;
|
|
|
|
es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_QCELP);
|
|
fmt.audio.i_rate = 8000;
|
|
fmt.audio.i_physical_channels = AOUT_CHAN_CENTER;
|
|
return vlc_rtp_pt_request_es(pt, &fmt);
|
|
}
|
|
|
|
static const struct vlc_rtp_pt_operations rtp_audio_qcelp = {
|
|
NULL, qcelp_init, codec_destroy, codec_decode,
|
|
};
|
|
|
|
/* Not using SDP, we need to guess the payload format used */
|
|
/* see http://www.iana.org/assignments/rtp-parameters */
|
|
void rtp_autodetect(vlc_object_t *obj, rtp_session_t *session,
|
|
const struct vlc_rtp_pt_owner *restrict owner)
|
|
{
|
|
char type[] = "audio", proto[] = "RTP/AVP";
|
|
char format[] = "0 3 8 10 11 12 14";
|
|
struct vlc_sdp_media media = {
|
|
.type = type, .port_count = 1, .proto = proto, .format = format };
|
|
|
|
vlc_rtp_add_media_types(obj, session, &media, owner);
|
|
strcpy(type, "video");
|
|
strcpy(format, "32 33");
|
|
vlc_rtp_add_media_types(obj, session, &media, owner);
|
|
}
|
|
|
|
/*
|
|
* Dynamic payload type handlers
|
|
*/
|
|
|
|
static struct vlc_rtp_pt *vlc_rtp_pt_create(vlc_object_t *obj,
|
|
const struct vlc_sdp_pt *desc,
|
|
const struct vlc_rtp_pt_owner *restrict owner)
|
|
{
|
|
if (desc->clock_rate == 0) {
|
|
/* Dynamic payload type not defined in the SDP */
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
struct vlc_rtp_pt *pt = malloc(sizeof (*pt));
|
|
if (unlikely(pt == NULL))
|
|
return NULL;
|
|
|
|
pt->owner = *owner;
|
|
pt->frequency = desc->clock_rate;
|
|
pt->channel_count = desc->channel_count;
|
|
|
|
if (vlc_rtp_pt_instantiate(obj, pt, desc) == 0)
|
|
return pt;
|
|
|
|
pt->ops = NULL;
|
|
|
|
if (strcmp(desc->media->type, "audio") == 0) {
|
|
if (strcmp(desc->name, "GSM") == 0)
|
|
pt->ops = &rtp_audio_gsm;
|
|
else if (strcmp(desc->name, "QCELP") == 0)
|
|
pt->ops = &rtp_audio_qcelp;
|
|
}
|
|
|
|
if (pt->ops == NULL) {
|
|
msg_Err(obj, "unsupported media type %s/%s", desc->media->type,
|
|
desc->name);
|
|
free(pt);
|
|
errno = ENOTSUP;
|
|
pt = NULL;
|
|
}
|
|
|
|
return pt;
|
|
}
|
|
|
|
void vlc_rtp_pt_release(struct vlc_rtp_pt *pt)
|
|
{
|
|
if (pt->ops->release != NULL)
|
|
pt->ops->release(pt);
|
|
free(pt);
|
|
}
|
|
|
|
struct vlc_sdp_pt_default {
|
|
unsigned char number;
|
|
char subtype[6];
|
|
unsigned char channel_count;
|
|
unsigned int clock_rate;
|
|
};
|
|
|
|
/**
|
|
* Sets the static payload types.
|
|
*/
|
|
static void vlc_rtp_set_default_types(struct vlc_sdp_pt *restrict types,
|
|
const struct vlc_sdp_media *media)
|
|
{
|
|
/* Implicit payload type mappings (RFC3551 section 6) */
|
|
static const struct vlc_sdp_pt_default audio_defaults[] = {
|
|
{ 0, "PCMU", 1, 8000, },
|
|
{ 3, "GSM", 1, 8000, },
|
|
{ 4, "G723", 1, 8000, },
|
|
{ 5, "DIV4", 1, 8000, },
|
|
{ 6, "DIV4", 1, 16000, },
|
|
{ 7, "LPC", 1, 8000, },
|
|
{ 8, "PCMA", 1, 8000, },
|
|
{ 9, "G722", 1, 8000, },
|
|
{ 10, "L16", 2, 44100, },
|
|
{ 11, "L16", 1, 44100, },
|
|
{ 12, "QCELP", 1, 8000, },
|
|
{ 13, "CN", 1, 8000, },
|
|
{ 14, "MPA", 0, 90000 },
|
|
{ 15, "G728", 1, 8000, },
|
|
{ 16, "DIV4", 1, 11025, },
|
|
{ 17, "DIV4", 1, 22050, },
|
|
{ 18, "G729", 1, 8000, },
|
|
{ 33, "MP2T", 0, 90000, },
|
|
};
|
|
static const struct vlc_sdp_pt_default video_defaults[] = {
|
|
{ 25, "CelB", 0, 90000, },
|
|
{ 26, "JPEG", 0, 90000, },
|
|
{ 28, "nv", 0, 90000, },
|
|
{ 31, "H261", 0, 90000, },
|
|
{ 32, "MPV", 0, 90000, },
|
|
{ 33, "MP2T", 0, 90000, },
|
|
{ 34, "H263", 0, 90000, },
|
|
};
|
|
const struct vlc_sdp_pt_default *defs = NULL;
|
|
size_t def_size = 0;
|
|
|
|
if (strcmp(media->type, "audio") == 0) {
|
|
defs = audio_defaults;
|
|
def_size = ARRAY_SIZE(audio_defaults);
|
|
} else if (strcmp(media->type, "video") == 0) {
|
|
defs = video_defaults;
|
|
def_size = ARRAY_SIZE(video_defaults);
|
|
}
|
|
|
|
for (size_t i = 0; i < def_size; i++) {
|
|
const struct vlc_sdp_pt_default *def = defs + i;
|
|
struct vlc_sdp_pt *pt = types + def->number;
|
|
|
|
pt->media = media;
|
|
strcpy(pt->name, def->subtype);
|
|
pt->clock_rate = def->clock_rate;
|
|
pt->channel_count = def->channel_count;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers all payload types declared in an SDP media.
|
|
*/
|
|
int vlc_rtp_add_media_types(vlc_object_t *obj, rtp_session_t *session,
|
|
const struct vlc_sdp_media *media,
|
|
const struct vlc_rtp_pt_owner *restrict owner)
|
|
{
|
|
struct vlc_sdp_pt types[128] = { };
|
|
|
|
vlc_rtp_set_default_types(types, media);
|
|
|
|
/* Parse the a=rtpmap and extract a=fmtp lines */
|
|
for (const struct vlc_sdp_attr *a = media->attrs; a != NULL; a = a->next) {
|
|
if (strcmp(a->name, "rtpmap") == 0) {
|
|
unsigned char number, channels;
|
|
char name[16];
|
|
unsigned int frequency;
|
|
|
|
switch (sscanf(a->value, "%hhu %15[^/]/%u/%hhu", &number, name,
|
|
&frequency, &channels)) {
|
|
case 3:
|
|
channels = 0;
|
|
/* fall through */
|
|
case 4:
|
|
if (number < ARRAY_SIZE(types)) {
|
|
types[number].media = media;
|
|
strcpy(types[number].name, name);
|
|
types[number].clock_rate = frequency;
|
|
types[number].channel_count = channels;
|
|
}
|
|
break;
|
|
}
|
|
|
|
} else if (strcmp(a->name, "fmtp") == 0) {
|
|
unsigned char number;
|
|
int offset;
|
|
|
|
if (sscanf(a->value, "%hhu %n", &number, &offset) == 1
|
|
&& number < ARRAY_SIZE(types))
|
|
types[number].parameters = a->value + offset;
|
|
}
|
|
}
|
|
|
|
const char *numbers = media->format; /* space-separated list of PTs */
|
|
char *end;
|
|
int errors = 0;
|
|
|
|
for (;;) {
|
|
unsigned long number = strtoul(numbers, &end, 10);
|
|
|
|
if (end == numbers) {
|
|
if (*end != '\0')
|
|
return -EINVAL;
|
|
break; /* garbage or end of the line */
|
|
}
|
|
|
|
numbers = end + strspn(end, " "); /* next PT number */
|
|
|
|
if (number >= ARRAY_SIZE(types))
|
|
continue;
|
|
|
|
struct vlc_sdp_pt *const type = types + number;
|
|
|
|
if (type->media == NULL) /* not defined or already used */
|
|
continue;
|
|
|
|
msg_Dbg(obj, "payload type %lu: %s/%s, %u Hz", number,
|
|
media->type, type->name, type->clock_rate);
|
|
if (type->channel_count > 0)
|
|
msg_Dbg(obj, " - %hhu channel(s)", type->channel_count);
|
|
if (type->parameters != NULL)
|
|
msg_Dbg(obj, " - parameters: %s", type->parameters);
|
|
|
|
struct vlc_rtp_pt *pt = vlc_rtp_pt_create(obj, type, owner);
|
|
if (pt != NULL) {
|
|
pt->number = number;
|
|
if (rtp_add_type(session, pt))
|
|
vlc_rtp_pt_release(pt);
|
|
} else
|
|
errors++;
|
|
|
|
type->media = NULL; /* Prevent duplicate PT numbers. */
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
static void es_dummy_destroy(struct vlc_rtp_es *es)
|
|
{
|
|
assert(es == vlc_rtp_es_dummy);
|
|
}
|
|
|
|
static void es_dummy_decode(struct vlc_rtp_es *es, block_t *block)
|
|
{
|
|
assert(es == vlc_rtp_es_dummy);
|
|
block_Release(block);
|
|
}
|
|
|
|
static const struct vlc_rtp_es_operations vlc_rtp_es_dummy_ops = {
|
|
es_dummy_destroy, es_dummy_decode,
|
|
};
|
|
|
|
static struct vlc_rtp_es vlc_rtp_es_dummy_instance = {
|
|
&vlc_rtp_es_dummy_ops,
|
|
};
|
|
|
|
struct vlc_rtp_es *const vlc_rtp_es_dummy = &vlc_rtp_es_dummy_instance;
|
|
|