Commit 4c20f3dc authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

RTP Vorbis payload format (incomplete)

(as per RFC queue'd draft-ietf-avt-rtp-vorbis-09).
Not tested against other implementations, and some pretty bad PTS
issues. RTP only provides us with a valid PTS for the first frame in a
packet, and we don't know the length of each frame, so, hmm... I wonder
how to compute the PTS for non-first frames...

Also, we would need either an SDP parser to use Vorbis (properly) as a
dynamic payload type, or to use a proprietary static payload type and a
proprietary static RTP clock frequency.
parent a48b3107
...@@ -600,131 +600,6 @@ static void *ts_init (demux_t *demux) ...@@ -600,131 +600,6 @@ static void *ts_init (demux_t *demux)
* Dynamic payload type handlers * Dynamic payload type handlers
* Hmm, none implemented yet. * Hmm, none implemented yet.
*/ */
#if 0
/* PT=dynamic
* vorbis: Xiph Vorbis audio (draft-ietf-avt-rtp-vorbis-09, RFC FIXME)
*/
typedef struct rtp_vorbis_t
{
es_out_id_t *id;
block_t *block;
} rtp_vorbis_t;
static void *vorbis_init (demux_t *demux)
{
es_format_t fmt;
rtp_vorbis_t *self = malloc (sizeof (*self));
if (self == NULL)
return NULL;
es_format_Init (&fmt, AUDIO_ES, VLC_FOURCC ('v', 'o', 'r', 'b'));
self->id = codec_init (demux, &fmt);
self->block = NULL;
return self;
}
static void vorbis_destroy (demux_t *demux, void *data)
{
rtp_vorbis_t *self = data;
if (!data)
return;
if (self->block)
{
self->block->i_flags |= BLOCK_FLAG_CORRUPTED;
codec_decode (demux, NULL, self->block);
}
codec_destroy (demux, self->id);
free (self);
}
static void vorbis_decode (demux_t *demux, void *data, block_t *block)
{
rtp_vorbis_t *self = data;
if (!data || block->i_buffer < 4)
goto drop;
/* 32-bits Vorbis RTP header */
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;
/* Vorbis RTP defragmentation */
if ((fragtype != 0) && (pkts > 0))
goto drop;
if (self->block && (block->i_flags & BLOCK_FLAG_DISCONTINUITY))
{ /* Screwed! discontinuity within a fragmented packet */
msg_Warn (demux, "discontinuity in fragmented Vorbis packet");
self->block->i_flags |= BLOCK_FLAG_CORRUPTED;
codec_decode (demux, NULL, self->block);
self->block = NULL;
}
if (fragtype <= 1)
{
if (self->block) /* Buggy sender! */
{
block_Release (self->block);
self->block = NULL;
}
if (fragtype == 1)
{
self->block = block;
return;
}
}
else
{
if (!self->block) /* Buggy sender! */
goto drop;
size_t len = self->block->i_buffer;
self->block = block_Realloc (self->block, 0, len + block->i_buffer);
if (self->block)
memcpy (self->block->p_buffer + len, block->p_buffer,
block->i_buffer);
block_Release (block);
if (fragtype == 2 || !self->block)
return;
/* End of fragment reached */
block = self->block;
self->block = NULL;
}
switch (datatype)
{
case 0:
msg_Dbg (demux, "Payload: raw");
break;
case 1:
msg_Dbg (demux, "Payload: configuration");
break;
case 2:
msg_Dbg (demux, "Payload: comment");
break;
default:
block_Release (block);
return;
}
msg_Dbg (demux, "Packets number %u", pkts);
msg_Dbg (demux, "Configuration %"PRIu32, ident);
codec_decode (demux, NULL, block);
return;
drop:
block_Release (block);
}
#endif
/** /**
* Processing callback * Processing callback
......
/**
* @file rtpxiph.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 General Public License
* as published by the Free Software Foundation; either version 2.0
* 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 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 <stdarg.h>
#include <assert.h>
#include <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_aout.h>
#include <vlc_network.h>
#ifdef HAVE_POLL
# include <poll.h>
#endif
#include <vlc_plugin.h>
#include <vlc_codecs.h>
#include "rtp.h"
#include <srtp.h>
/* PT=dynamic
* vorbis: Xiph Vorbis audio (draft-ietf-avt-rtp-vorbis-09, RFC FIXME)
*/
typedef struct rtp_vorbis_t
{
es_out_id_t *id;
block_t *block;
uint32_t ident;
} rtp_vorbis_t;
static void *vorbis_init (demux_t *demux)
{
rtp_vorbis_t *self = malloc (sizeof (*self));
if (self == NULL)
return NULL;
self->id = NULL;
self->block = NULL;
self->ident = 0xffffffff; /* impossible value on the wire */
(void)demux;
return self;
}
static void vorbis_destroy (demux_t *demux, void *data)
{
rtp_vorbis_t *self = data;
if (!data)
return;
if (self->block)
{
self->block->i_flags |= BLOCK_FLAG_CORRUPTED;
codec_decode (demux, self->id, self->block);
}
codec_destroy (demux, self->id);
free (self);
}
/* Convert configuration from RTP to VLC format */
static ssize_t vorbis_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 */
uint8_t *extra = malloc ((size_t)6 + idlen + cmtlen + setuplen);
if (extra == NULL)
return -1;
uint8_t *ptr = *pextra = extra;
/* Identification header */
*ptr++ = idlen >> 8;
*ptr++ = idlen & 0xff;
memcpy (ptr, buf, idlen);
buf += idlen;
ptr += idlen;
/* Comments header */
*ptr++ = cmtlen >> 8;
*ptr++ = cmtlen & 0xff;
memcpy (ptr, buf, cmtlen);
buf += cmtlen;
ptr += cmtlen;
/* Setup header */
*ptr++ = setuplen >> 8;
*ptr++ = setuplen & 0xff;
memcpy (ptr, buf, setuplen);
ptr += setuplen;
return ptr - extra;
}
static void vorbis_decode (demux_t *demux, void *data, block_t *block)
{
rtp_vorbis_t *self = data;
if (!data || block->i_buffer < 4)
goto drop;
/* 32-bits Vorbis 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;
/* Vorbis RTP defragmentation */
if (self->block && (block->i_flags & BLOCK_FLAG_DISCONTINUITY))
{ /* Screwed! discontinuity within a fragmented packet */
msg_Warn (demux, "discontinuity in fragmented Vorbis 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;
}
/* Vorbis 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 audio frame */
{
if (self->ident != ident)
break; /* Ignore raw without configuration */
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 */
codec_decode (demux, 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 = vorbis_header (&extv, block->p_buffer, len);
if (extc < 0)
break;
es_format_t fmt;
es_format_Init (&fmt, AUDIO_ES, VLC_FOURCC ('v','o','r','b'));
fmt.p_extra = extv;
fmt.i_extra = extc;
codec_destroy (demux, self->id);
msg_Dbg (demux, "Vorbis packed configuration received "
"(%06"PRIx32")", ident);
self->ident = ident;
self->id = codec_init (demux, &fmt);
break;
}
}
block->i_buffer -= len;
block->p_buffer += len;
pkts--;
}
drop:
block_Release (block);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment