/**
* @file xiph.c
* @brief Real-Time Protocol (RTP) Xiph payloads receival
*/
/*****************************************************************************
* Copyright © 2008 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 <assert.h>
#include <stdlib.h>
#include <string.h>
#include <vlc_common.h>
#include <vlc_block.h>
#include <vlc_es.h>
#include <vlc_plugin.h>
#include "../../demux/xiph.h"
#include "rtp.h"
struct rtp_xiph {
vlc_object_t *obj;
enum es_format_category_e cat;
vlc_fourcc_t fourcc;
};
struct rtp_xiph_source {
struct vlc_rtp_es *id;
block_t *block;
uint32_t ident;
};
static void *xiph_init(struct vlc_rtp_pt *pt)
{
struct rtp_xiph_source *self = malloc(sizeof (*self));
if (self == NULL)
return NULL;
self->id = NULL;
self->block = NULL;
self->ident = 0xffffffff; /* impossible value on the wire */
(void) pt;
return self;
}
static void xiph_destroy(struct vlc_rtp_pt *pt, void *data)
{
struct rtp_xiph_source *self = data;
if (!data)
return;
if (self->block)
{
self->block->i_flags |= BLOCK_FLAG_CORRUPTED;
vlc_rtp_es_send(self->id, self->block);
}
vlc_rtp_es_destroy(self->id);
free (self);
(void) pt;
}
/* Convert configuration from RTP to VLC format */
static ssize_t xiph_header (void **pextra, const uint8_t *buf, size_t len)
{
/* Headers number */
if (len == 0)
return -1; /* Invalid */
unsigned hcount = 1 + *buf++;
len--;
if (hcount != 3)
return -1; /* Invalid */
/* Header lengths */
uint16_t idlen = 0, cmtlen = 0, setuplen = 0;
do
{
if (len == 0)
return -1;
idlen = (idlen << 7) | (*buf & 0x7f);
len--;
}
while (*buf++ & 0x80);
do
{
if (len == 0)
return -1;
cmtlen = (cmtlen << 7) | (*buf & 0x7f);
len--;
}
while (*buf++ & 0x80);
if (len < idlen + cmtlen)
return -1;
setuplen = len - (idlen + cmtlen);
/* Create the VLC extra format header */
size_t sizes[3] = {
idlen, cmtlen, setuplen
};
const void *payloads[3] = {
buf + 0,
buf + idlen,
buf + idlen + cmtlen
};
void *extra;
size_t extra_size;
if (xiph_PackHeaders (&extra_size, &extra, sizes, payloads, 3))
return -1;
*pextra = extra;
return extra_size;
}
static void xiph_decode(struct vlc_rtp_pt *pt, void *data, block_t *block,
const struct vlc_rtp_pktinfo *restrict info)
{
struct rtp_xiph_source *self = data;
struct rtp_xiph *sys = pt->opaque;
if (!data || block->i_buffer < 4)
goto drop;
/* 32-bits RTP header (§2.2) */
uint32_t ident = GetDWBE (block->p_buffer);
block->i_buffer -= 4;
block->p_buffer += 4;
unsigned fragtype = (ident >> 6) & 3;
unsigned datatype = (ident >> 4) & 3;
unsigned pkts = (ident) & 15;
ident >>= 8;
/* RTP defragmentation */
if (self->block && (block->i_flags & BLOCK_FLAG_DISCONTINUITY))
{ /* Screwed! discontinuity within a fragmented packet */
msg_Warn(sys->obj, "discontinuity in fragmented Xiph packet");
block_Release (self->block);
self->block = NULL;
}
if (fragtype <= 1)
{
if (self->block) /* Invalid first fragment */
{
block_Release (self->block);
self->block = NULL;
}
}
else
{
if (!self->block)
goto drop; /* Invalid non-first fragment */
}
if (fragtype > 0)
{ /* Fragment */
if (pkts > 0 || block->i_buffer < 2)
goto drop;
size_t fraglen = GetWBE (block->p_buffer);
if (block->i_buffer < (fraglen + 2))
goto drop; /* Invalid payload length */
block->i_buffer = fraglen;
if (fragtype == 1)/* Keep first fragment */
{
block->i_buffer += 2;
self->block = block;
}
else
{ /* Append non-first fragment */
size_t len = self->block->i_buffer;
self->block = block_Realloc (self->block, 0, len + fraglen);
if (!self->block)
{
block_Release (block);
return;
}
memcpy (self->block->p_buffer + len, block->p_buffer + 2,
fraglen);
block_Release (block);
}
if (fragtype < 3)
return; /* Non-last fragment */
/* Last fragment reached, process it */
block = self->block;
self->block = NULL;
SetWBE (block->p_buffer, block->i_buffer - 2);
pkts = 1;
}
/* RTP payload packets processing */
while (pkts > 0)
{
if (block->i_buffer < 2)
goto drop;
size_t len = GetWBE (block->p_buffer);
block->i_buffer -= 2;
block->p_buffer += 2;
if (block->i_buffer < len)
goto drop;
switch (datatype)
{
case 0: /* Raw payload */
{
if (self->ident != ident)
{
msg_Warn(sys->obj,
"ignoring raw payload without configuration");
break;
}
block_t *raw = block_Alloc (len);
memcpy (raw->p_buffer, block->p_buffer, len);
raw->i_pts = block->i_pts; /* FIXME: what about pkts > 1 */
block->i_dts = VLC_TICK_INVALID;
vlc_rtp_es_send(self->id, raw);
break;
}
case 1: /* Packed configuration frame (§3.1.1) */
{
if (self->ident == ident)
break; /* Ignore config retransmission */
void *extv;
ssize_t extc = xiph_header (&extv, block->p_buffer, len);
if (extc < 0)
break;
es_format_t fmt;
es_format_Init(&fmt, sys->cat, sys->fourcc);
fmt.p_extra = extv;
fmt.i_extra = extc;
vlc_rtp_es_destroy(self->id);
msg_Dbg(sys->obj,
"packed configuration received (%06"PRIx32")", ident);
self->ident = ident;
self->id = vlc_rtp_pt_request_es(pt, &fmt);
break;
}
}
block->i_buffer -= len;
block->p_buffer += len;
pkts--;
}
(void) info;
drop:
block_Release (block);
}
static void xiph_release(struct vlc_rtp_pt *pt)
{
free(pt->opaque);
}
static const struct vlc_rtp_pt_operations rtp_xiph_ops = {
xiph_release, xiph_init, xiph_destroy, xiph_decode,
};
static int xiph_open(vlc_object_t *obj, struct vlc_rtp_pt *pt,
const struct vlc_sdp_pt *desc,
int cat, vlc_fourcc_t fourcc)
{
struct rtp_xiph *sys = malloc(sizeof (*sys));
if (unlikely(sys == NULL))
return VLC_ENOMEM;
sys->obj = obj;
sys->cat = cat;
sys->fourcc = fourcc;
pt->opaque = sys;
pt->ops = &rtp_xiph_ops;
(void) desc;
return VLC_SUCCESS;
}
/* Xiph Vorbis audio (RFC 5215) */
static int vorbis_open(vlc_object_t *obj, struct vlc_rtp_pt *pt,
const struct vlc_sdp_pt *desc)
{
return xiph_open(obj, pt, desc, AUDIO_ES, VLC_CODEC_VORBIS);
}
/* Xiph Theora video (I-D draft-barbato-avt-rtp-theora-01) */
static int theora_open(vlc_object_t *obj, struct vlc_rtp_pt *pt,
const struct vlc_sdp_pt *desc)
{
return xiph_open(obj, pt, desc, VIDEO_ES, VLC_CODEC_THEORA);
}
vlc_module_begin()
set_shortname(N_("RTP Xiph"))
set_description(N_("RTP Xiph payload parser"))
set_subcategory(SUBCAT_INPUT_DEMUX)
set_capability("rtp audio parser", 0)
set_callback(vorbis_open)
add_shortcut("vorbis")
add_submodule()
set_capability("rtp video parser", 0)
set_callback(theora_open)
add_shortcut("theora")
vlc_module_end()
¤ Dauer der Verarbeitung: 0.3 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|