[vlc-devel] [PATCH] Support the Metacube protocol when streaming over HTTP.

Steinar H. Gunderson steinar+vlc at gunderson.no
Tue Sep 17 01:35:54 CEST 2013


This is an inclusion of a patch that has been living out-of-tree for some time,
but now as Cubemap 1.0.0 has been released and the protocol has been frozen,
it would sound reasonable to get it into mainline.

Essentially it makes the VLC HTTP server support the Metacube protocol, which
is a framing format to let an external reflector know where the start and end
of the blocks are without having to understand the format itself (similar to
how the VLC HTTP server itself gets to know this information). This lets it
serve the correct header block to new clients joining in the middle of the
stream, and makes sure new clients don't start in the middle of a block,
which is a problem for non-self-synchronizing formats (such as FLV and WebM).
It includes self-synchronization and header checksumming to ensure smooth
recovery from overruns in the server-to-reflector TCP stream.

The only consumer of this protocol currently, and for the foreseeable future,
is Cubemap (http://git.sesse.net/?p=cubemap), a scalable video reflector
designed to be used with VLC.
---
 include/metacube2.h          |   27 +++++++++++++++++
 include/vlc_httpd.h          |    2 +-
 modules/access_output/http.c |   13 ++++++--
 src/Makefile.am              |    1 +
 src/network/httpd.c          |   44 ++++++++++++++++++++++++---
 src/network/metacube2.c      |   69 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 149 insertions(+), 7 deletions(-)
 create mode 100644 include/metacube2.h
 create mode 100644 src/network/metacube2.c

diff --git a/include/metacube2.h b/include/metacube2.h
new file mode 100644
index 0000000..dfbfd24
--- /dev/null
+++ b/include/metacube2.h
@@ -0,0 +1,27 @@
+#ifndef _METACUBE2_H
+#define _METACUBE2_H
+
+/*
+ * Definitions for the Metacube2 protocol, used to communicate with Cubemap.
+ *
+ * Note: This file is meant to compile as both C and C++, for easier inclusion
+ * in other projects.
+ */
+
+#include <stdint.h>
+
+#define METACUBE2_SYNC "cube!map"  /* 8 bytes long. */
+#define METACUBE_FLAGS_HEADER 0x1
+#define METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START 0x2
+
+struct metacube2_block_header {
+	char sync[8];    /* METACUBE2_SYNC */
+	uint32_t size;   /* Network byte order. Does not include header. */
+	uint16_t flags;  /* Network byte order. METACUBE_FLAGS_*. */
+	uint16_t csum;   /* Network byte order. CRC16 of size and flags. */
+};
+
+/* This code is based on code generated by pycrc. */
+uint16_t metacube2_compute_crc(const struct metacube2_block_header *hdr);
+
+#endif  /* !defined(_METACUBE_H) */
diff --git a/include/vlc_httpd.h b/include/vlc_httpd.h
index 9eb0b15..8eacf68 100644
--- a/include/vlc_httpd.h
+++ b/include/vlc_httpd.h
@@ -135,7 +135,7 @@ VLC_API void httpd_RedirectDelete( httpd_redirect_t * );
 
 
 typedef struct httpd_stream_t httpd_stream_t;
-VLC_API httpd_stream_t * httpd_StreamNew( httpd_host_t *, const char *psz_url, const char *psz_mime, const char *psz_user, const char *psz_password ) VLC_USED;
+VLC_API httpd_stream_t * httpd_StreamNew( httpd_host_t *, const char *psz_url, const char *psz_mime, const char *psz_user, const char *psz_password, bool b_metacube ) VLC_USED;
 VLC_API void httpd_StreamDelete( httpd_stream_t * );
 VLC_API int httpd_StreamHeader( httpd_stream_t *, uint8_t *p_data, int i_data );
 VLC_API int httpd_StreamSend( httpd_stream_t *, const block_t *p_block );
diff --git a/modules/access_output/http.c b/modules/access_output/http.c
index bf75130..e608f8d 100644
--- a/modules/access_output/http.c
+++ b/modules/access_output/http.c
@@ -57,6 +57,9 @@ static void Close( vlc_object_t * );
 #define MIME_TEXT N_("Mime")
 #define MIME_LONGTEXT N_("MIME returned by the server (autodetected " \
                         "if not specified)." )
+#define METACUBE_TEXT N_("Metacube")
+#define METACUBE_LONGTEXT N_("Use the Metacube protocol. Needed for streaming " \
+                             "to the Cubemap reflector.")
 
 
 vlc_module_begin ()
@@ -72,6 +75,8 @@ vlc_module_begin ()
                   PASS_TEXT, PASS_LONGTEXT, true )
     add_string( SOUT_CFG_PREFIX "mime", "",
                 MIME_TEXT, MIME_LONGTEXT, true )
+    add_bool( SOUT_CFG_PREFIX "metacube", false,
+              METACUBE_TEXT, METACUBE_LONGTEXT, true )
     set_callbacks( Open, Close )
 vlc_module_end ()
 
@@ -80,7 +85,7 @@ vlc_module_end ()
  * Exported prototypes
  *****************************************************************************/
 static const char *const ppsz_sout_options[] = {
-    "user", "pwd", "mime", NULL
+    "user", "pwd", "mime", "metacube", NULL
 };
 
 static ssize_t Write( sout_access_out_t *, block_t * );
@@ -114,6 +119,8 @@ static int Open( vlc_object_t *p_this )
     char                *psz_pwd;
     char                *psz_mime;
 
+    bool                b_metacube;
+
     if( !( p_sys = p_access->p_sys =
                 malloc( sizeof( sout_access_out_sys_t ) ) ) )
         return VLC_ENOMEM ;
@@ -189,9 +196,11 @@ static int Open( vlc_object_t *p_this )
         psz_mime = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "mime" );
     }
 
+    b_metacube = var_GetBool( p_access, SOUT_CFG_PREFIX "metacube" );
+
     p_sys->p_httpd_stream =
         httpd_StreamNew( p_sys->p_httpd_host, path, psz_mime,
-                         psz_user, psz_pwd );
+                         psz_user, psz_pwd, b_metacube );
     free( psz_user );
     free( psz_pwd );
     free( psz_mime );
diff --git a/src/Makefile.am b/src/Makefile.am
index 639d157..89f78bc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -480,6 +480,7 @@ SOURCES_libvlc_common = \
 
 SOURCES_libvlc_httpd = \
 	network/httpd.c \
+	network/metacube2.c \
 	$(NULL)
 
 SOURCES_libvlc_sout = \
diff --git a/src/network/httpd.c b/src/network/httpd.c
index ff7e653..b8f6768 100644
--- a/src/network/httpd.c
+++ b/src/network/httpd.c
@@ -40,6 +40,7 @@
 #include <vlc_url.h>
 #include <vlc_mime.h>
 #include <vlc_block.h>
+#include <metacube2.h>
 #include "../libvlc.h"
 
 #include <string.h>
@@ -630,6 +631,7 @@ struct httpd_stream_t
     httpd_url_t *url;
 
     char    *psz_mime;
+    bool    b_metacube;
 
     /* Header to send as first packet */
     uint8_t *p_header;
@@ -739,9 +741,25 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys,
             /* Send the header */
             if( stream->i_header > 0 )
             {
-                answer->i_body = stream->i_header;
-                answer->p_body = xmalloc( stream->i_header );
-                memcpy( answer->p_body, stream->p_header, stream->i_header );
+                if ( stream->b_metacube )
+                {
+                    struct metacube2_block_header hdr;
+                    memcpy( hdr.sync, METACUBE2_SYNC, sizeof(METACUBE2_SYNC) );
+                    hdr.size = htonl( stream->i_header );
+                    hdr.flags = htons( METACUBE_FLAGS_HEADER );
+                    hdr.csum = htons( metacube2_compute_crc( &hdr ) );
+
+                    answer->i_body = stream->i_header + sizeof( hdr );
+                    answer->p_body = xmalloc( answer->i_body );
+                    memcpy( answer->p_body, &hdr, sizeof( hdr ) );
+                    memcpy( answer->p_body + sizeof( hdr ), stream->p_header, stream->i_header );
+                }
+                else
+                {
+                    answer->i_body = stream->i_header;
+                    answer->p_body = xmalloc( stream->i_header );
+                    memcpy( answer->p_body, stream->p_header, stream->i_header );
+                }
             }
             answer->i_body_offset = stream->i_buffer_last_pos;
             if( stream->b_has_keyframes )
@@ -789,13 +807,16 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys,
             httpd_MsgAdd( answer, "Content-type",  "%s", stream->psz_mime );
         }
         httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
+        if( stream->b_metacube )
+            httpd_MsgAdd( answer, "Content-encoding",  "metacube");
         return VLC_SUCCESS;
     }
 }
 
 httpd_stream_t *httpd_StreamNew( httpd_host_t *host,
                                  const char *psz_url, const char *psz_mime,
-                                 const char *psz_user, const char *psz_password )
+                                 const char *psz_user, const char *psz_password,
+                                 bool b_metacube )
 {
     httpd_stream_t *stream = xmalloc( sizeof( httpd_stream_t ) );
 
@@ -814,6 +835,7 @@ httpd_stream_t *httpd_StreamNew( httpd_host_t *host,
     {
         stream->psz_mime = strdup( vlc_mime_Ext2Mime( psz_url ) );
     }
+    stream->b_metacube = b_metacube;
     stream->i_header = 0;
     stream->p_header = NULL;
     stream->i_buffer_size = 5000000;    /* 5 Mo per stream */
@@ -890,6 +912,20 @@ int httpd_StreamSend( httpd_stream_t *stream, const block_t *p_block )
         stream->i_last_keyframe_seen_pos = stream->i_buffer_pos;
     }
 
+    if( stream->b_metacube )
+    {
+        struct metacube2_block_header hdr;
+        memcpy( hdr.sync, METACUBE2_SYNC, sizeof(METACUBE2_SYNC) );
+        hdr.size = htonl( p_block->i_buffer );
+        hdr.flags = htons( 0 );
+        if( p_block->i_flags & BLOCK_FLAG_HEADER )
+            hdr.flags |= htons( METACUBE_FLAGS_HEADER );
+        if( stream->b_has_keyframes && !( p_block->i_flags & BLOCK_FLAG_TYPE_I ) )
+            hdr.flags |= htons( METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START );
+        hdr.csum = htons( metacube2_compute_crc( &hdr ) );
+        httpd_AppendData( stream, (uint8_t *)&hdr, sizeof(hdr) );
+    }
+
     httpd_AppendData( stream, p_block->p_buffer, p_block->i_buffer );
 
     vlc_mutex_unlock( &stream->lock );
diff --git a/src/network/metacube2.c b/src/network/metacube2.c
new file mode 100644
index 0000000..353ce88
--- /dev/null
+++ b/src/network/metacube2.c
@@ -0,0 +1,69 @@
+/*
+ * Implementation of Metacube2 utility functions. Taken from the Cubemap
+ * distribution and then relicensed by the author to LGPL2.1+ for inclusion
+ * into VLC.
+ *
+ * Note: This file is meant to compile as both C and C++, for easier inclusion
+ * in other projects.
+ *
+ * Copyright (C) 2013 Steinar H. Gunderson
+ *
+ * Author: Steinar H. Gunderson <steinar+vlc at gunderson.no>
+ *
+ * This program 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 program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+
+#include "metacube2.h"
+
+/*
+ * https://www.ece.cmu.edu/~koopman/pubs/KoopmanCRCWebinar9May2012.pdf
+ * recommends this for messages as short as ours (see table at page 34).
+ */
+#define METACUBE2_CRC_POLYNOMIAL 0x8FDB
+
+/* Semi-random starting value to make sure all-zero won't pass. */
+#define METACUBE2_CRC_START 0x1234
+
+uint16_t metacube2_compute_crc(const struct metacube2_block_header *hdr)
+{
+	static const int data_len = sizeof(hdr->size) + sizeof(hdr->flags);
+	const uint8_t *data = (uint8_t *)&hdr->size;
+	uint16_t crc = METACUBE2_CRC_START;
+	int i, j;
+
+	for (i = 0; i < data_len; ++i) {
+		uint8_t c = data[i];
+		for (j = 0; j < 8; j++) {
+			int bit = crc & 0x8000;
+			crc = (crc << 1) | ((c >> (7 - j)) & 0x01);
+			if (bit) {
+				crc ^= METACUBE2_CRC_POLYNOMIAL;
+			}
+		}
+	}
+
+	/* Finalize. */
+	for (i = 0; i < 16; i++) {
+		int bit = crc & 0x8000;
+		crc = crc << 1;
+		if (bit) {
+			crc ^= METACUBE2_CRC_POLYNOMIAL;
+		}
+	}
+
+	return crc;
+}
-- 
1.7.10.4




More information about the vlc-devel mailing list