]> git.sesse.net Git - vlc/commitdiff
RTP Vorbis payload format (incomplete)
authorRémi Denis-Courmont <rdenis@simphalempin.com>
Tue, 10 Jun 2008 16:54:33 +0000 (19:54 +0300)
committerRémi Denis-Courmont <rdenis@simphalempin.com>
Tue, 10 Jun 2008 17:05:07 +0000 (20:05 +0300)
(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.

modules/demux/rtp.c
modules/demux/rtpxiph.c [new file with mode: 0644]

index 0194518c6736db863e45ff25ba4a4fe8ab998517..8f917efce2ee6cdc4ad55d073cf54f6cf4499c29 100644 (file)
@@ -600,131 +600,6 @@ static void *ts_init (demux_t *demux)
  * Dynamic payload type handlers
  * 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
diff --git a/modules/demux/rtpxiph.c b/modules/demux/rtpxiph.c
new file mode 100644 (file)
index 0000000..7f6492e
--- /dev/null
@@ -0,0 +1,272 @@
+/**
+ * @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);
+}