X-Git-Url: https://git.sesse.net/?p=cubemap;a=blobdiff_plain;f=vlc-metacube.diff;fp=vlc-metacube.diff;h=fbde59d1f0e3bbe2bd38b7b453b2c74c2f5aa967;hp=0d965a1a3d32d4d5959a5d80244f32cf8a4048f6;hb=979a284b4039b0ea74525b700b9f1089b8c4248d;hpb=70c0baf4bcec3a77f0626d5a7bfde87fc7339698 diff --git a/vlc-metacube.diff b/vlc-metacube.diff index 0d965a1..fbde59d 100644 --- a/vlc-metacube.diff +++ b/vlc-metacube.diff @@ -1,5 +1,38 @@ +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 ++ ++#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 6100dd0..46d557a 100644 +index 6100dd0..7a14515 100644 --- a/include/vlc_httpd.h +++ b/include/vlc_httpd.h @@ -135,10 +135,10 @@ VLC_API void httpd_RedirectDelete( httpd_redirect_t * ); @@ -11,12 +44,12 @@ index 6100dd0..46d557a 100644 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 *, uint8_t *p_data, int i_data ); -+VLC_API int httpd_StreamSend( httpd_stream_t *, uint8_t *p_data, int i_data, bool b_header ); ++VLC_API int httpd_StreamSend( httpd_stream_t *, uint8_t *p_data, int i_data, bool b_header, bool b_keyframe ); /* Msg functions facilities */ diff --git a/modules/access_output/http.c b/modules/access_output/http.c -index 61095f5..95dd705 100644 +index 364768c..f667e08 100644 --- a/modules/access_output/http.c +++ b/modules/access_output/http.c @@ -72,6 +72,8 @@ vlc_module_begin () @@ -46,13 +79,12 @@ index 61095f5..95dd705 100644 if( !( p_sys = p_access->p_sys = malloc( sizeof( sout_access_out_sys_t ) ) ) ) return VLC_ENOMEM ; -@@ -188,10 +192,12 @@ static int Open( vlc_object_t *p_this ) - { +@@ -189,9 +193,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" ); ++ 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 ); @@ -60,39 +92,48 @@ index 61095f5..95dd705 100644 free( psz_user ); free( psz_pwd ); free( psz_mime ); -@@ -263,8 +269,9 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) +@@ -263,8 +269,10 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) while( p_buffer ) { block_t *p_next; -- -- if( p_buffer->i_flags & BLOCK_FLAG_HEADER ) + bool b_header_block = p_buffer->i_flags & BLOCK_FLAG_HEADER; -+ ++ bool b_keyframe = p_buffer->i_flags & BLOCK_FLAG_TYPE_I; + +- if( p_buffer->i_flags & BLOCK_FLAG_HEADER ) + if( b_header_block ) { /* gather header */ if( p_sys->b_header_complete ) -@@ -295,9 +302,10 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) - } - +@@ -297,7 +305,7 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) i_len += p_buffer->i_buffer; -+ /* send data */ i_err = httpd_StreamSend( p_sys->p_httpd_stream, p_buffer->p_buffer, - p_buffer->i_buffer ); -+ p_buffer->i_buffer, b_header_block ); ++ p_buffer->i_buffer, b_header_block, b_keyframe ); p_next = p_buffer->p_next; block_Release( p_buffer ); +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 f76c47c..dcda968 100644 +index bbfbd18..8e0b130 100644 --- a/src/network/httpd.c +++ b/src/network/httpd.c @@ -39,6 +39,7 @@ #include #include #include -+#include ++#include #include "../libvlc.h" #include @@ -104,7 +145,21 @@ index f76c47c..dcda968 100644 /* each host run in his own thread */ struct httpd_host_t -@@ -621,6 +623,7 @@ struct httpd_stream_t +@@ -159,6 +161,13 @@ struct httpd_client_t + int i_buffer; + uint8_t *p_buffer; + ++ /* ++ * If waiting for a keyframe, this is the position (in bytes) of the ++ * last keyframe the stream saw before this client connected. ++ * Otherwise, -1. ++ */ ++ int64_t i_keyframe_wait_to_pass; ++ + /* */ + httpd_message_t query; /* client -> httpd */ + httpd_message_t answer; /* httpd -> client */ +@@ -621,11 +630,20 @@ struct httpd_stream_t httpd_url_t *url; char *psz_mime; @@ -112,7 +167,37 @@ index f76c47c..dcda968 100644 /* Header to send as first packet */ uint8_t *p_header; -@@ -712,9 +715,24 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, + int i_header; + ++ /* Some muxes, in particular the avformat mux, can mark given blocks ++ * as keyframes, to ensure that the stream starts with one. ++ * (This is particularly important for WebM streaming to certain ++ * browsers.) Store if we've ever seen any such keyframe blocks, ++ * and if so, the byte position of the start of the last one. */ ++ bool b_has_keyframes; ++ int64_t i_last_keyframe_seen_pos; ++ + /* circular buffer */ + int i_buffer_size; /* buffer size, can't be reallocated smaller */ + uint8_t *p_buffer; /* buffer */ +@@ -659,6 +677,16 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, + /* fprintf( stderr, "httpd_StreamCallBack: no data\n" ); */ + return VLC_EGENERIC; /* wait, no data available */ + } ++ if( cl->i_keyframe_wait_to_pass >= 0 ) ++ { ++ if( stream->i_last_keyframe_seen_pos <= cl->i_keyframe_wait_to_pass ) ++ /* still waiting for the next keyframe */ ++ return VLC_EGENERIC; ++ ++ /* seek to the new keyframe */ ++ answer->i_body_offset = stream->i_last_keyframe_seen_pos; ++ cl->i_keyframe_wait_to_pass = -1; ++ } + if( answer->i_body_offset + stream->i_buffer_size < + stream->i_buffer_pos ) + { +@@ -712,11 +740,31 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, /* Send the header */ if( stream->i_header > 0 ) { @@ -121,10 +206,11 @@ index f76c47c..dcda968 100644 - memcpy( answer->p_body, stream->p_header, stream->i_header ); + if ( stream->b_metacube ) + { -+ struct metacube_block_header hdr; -+ memcpy( hdr.sync, METACUBE_SYNC, sizeof(METACUBE_SYNC) ); ++ struct metacube2_block_header hdr; ++ memcpy( hdr.sync, METACUBE2_SYNC, sizeof(METACUBE2_SYNC) ); + hdr.size = htonl( stream->i_header ); -+ hdr.flags = htonl( METACUBE_FLAGS_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 ); @@ -139,15 +225,19 @@ index f76c47c..dcda968 100644 + } } answer->i_body_offset = stream->i_buffer_last_pos; ++ if( stream->b_has_keyframes ) ++ cl->i_keyframe_wait_to_pass = stream->i_last_keyframe_seen_pos; ++ else ++ cl->i_keyframe_wait_to_pass = -1; vlc_mutex_unlock( &stream->lock ); -@@ -758,13 +776,18 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, + } + else +@@ -758,13 +806,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 ) -+ { ++ if( stream->b_metacube ) + httpd_MsgAdd( answer, "Content-encoding", "metacube"); -+ } return VLC_SUCCESS; } } @@ -160,7 +250,7 @@ index f76c47c..dcda968 100644 { httpd_stream_t *stream = xmalloc( sizeof( httpd_stream_t ) ); -@@ -783,6 +806,7 @@ httpd_stream_t *httpd_StreamNew( httpd_host_t *host, +@@ -783,6 +834,7 @@ httpd_stream_t *httpd_StreamNew( httpd_host_t *host, { stream->psz_mime = strdup( vlc_mime_Ext2Mime( psz_url ) ); } @@ -168,7 +258,16 @@ index f76c47c..dcda968 100644 stream->i_header = 0; stream->p_header = NULL; stream->i_buffer_size = 5000000; /* 5 Mo per stream */ -@@ -819,22 +843,10 @@ int httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data ) +@@ -791,6 +843,8 @@ httpd_stream_t *httpd_StreamNew( httpd_host_t *host, + * (this way i_body_offset can never be 0) */ + stream->i_buffer_pos = 1; + stream->i_buffer_last_pos = 1; ++ stream->b_has_keyframes = false; ++ stream->i_last_keyframe_seen_pos = 0; + + httpd_UrlCatch( stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack, + (httpd_callback_sys_t*)stream ); +@@ -819,22 +873,10 @@ int httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data ) return VLC_SUCCESS; } @@ -194,13 +293,13 @@ index f76c47c..dcda968 100644 while( i_count > 0) { int i_copy; -@@ -850,6 +862,31 @@ int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data ) +@@ -850,6 +892,40 @@ int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data ) } stream->i_buffer_pos += i_data; +} + -+int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data, bool b_header ) ++int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data, bool b_header, bool b_keyframe ) +{ + if( i_data < 0 || p_data == NULL ) + { @@ -211,41 +310,90 @@ index f76c47c..dcda968 100644 + /* save this pointer (to be used by new connection) */ + stream->i_buffer_last_pos = stream->i_buffer_pos; + -+ if ( stream->b_metacube ) { -+ struct metacube_block_header hdr; -+ memcpy( hdr.sync, METACUBE_SYNC, sizeof(METACUBE_SYNC) ); ++ if( b_keyframe ) ++ { ++ stream->b_has_keyframes = true; ++ 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( i_data ); -+ if ( b_header ) { -+ hdr.flags = htonl( METACUBE_FLAGS_HEADER ); -+ } else { -+ hdr.flags = htonl( 0 ); -+ } ++ hdr.flags = htons( 0 ); ++ if( b_header ) ++ hdr.flags |= htons( METACUBE_FLAGS_HEADER ); ++ if( stream->b_has_keyframes && !b_keyframe ) ++ 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_data, i_data ); vlc_mutex_unlock( &stream->lock ); return VLC_SUCCESS; -diff --git a/include/metacube.h b/include/metacube.h +@@ -1273,6 +1349,7 @@ static void httpd_ClientInit( httpd_client_t *cl, mtime_t now ) + cl->i_buffer_size = HTTPD_CL_BUFSIZE; + cl->i_buffer = 0; + cl->p_buffer = xmalloc( cl->i_buffer_size ); ++ cl->i_keyframe_wait_to_pass = -1; + cl->b_stream_mode = false; + + httpd_MsgInit( &cl->query ); +diff --git a/src/network/metacube2.c b/src/network/metacube2.c new file mode 100644 -index 0000000..b40a42e +index 0000000..7b2dacf --- /dev/null -+++ b/include/metacube.h -@@ -0,0 +1,17 @@ -+#ifndef _METACUBE_H -+#define _METACUBE_H -+ -+/* Definitions for the Metacube protocol, used to communicate with Cubemap. */ ++++ b/src/network/metacube2.c +@@ -0,0 +1,49 @@ ++/* ++ * Implementation of Metacube2 utility functions. ++ * ++ * Note: This file is meant to compile as both C and C++, for easier inclusion ++ * in other projects. ++ */ + +#include + -+#define METACUBE_SYNC "\\o/_metacube_\\o/" /* 16 bytes long. */ -+#define METACUBE_FLAGS_HEADER 0x1 ++#include "metacube2.h" + -+struct metacube_block_header { -+ char sync[16]; /* METACUBE_SYNC */ -+ uint32_t size; /* Network byte order. Does not include header. */ -+ uint32_t flags; /* Network byte order. METACUBE_FLAGS_*. */ -+}; ++/* ++ * 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 + -+#endif /* !defined(_METACUBE_H) */ ++/* 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; ++}