+static void OggGetSkeletonIndex( uint8_t **pp_buffer, long *pi_size, ogg_stream_t *p_stream )
+{
+ uint8_t *p_buffer = calloc( INDEX_BASE_SIZE + p_stream->skeleton.i_index_size, sizeof(uint8_t) );
+ if ( !p_buffer ) return;
+ *pp_buffer = p_buffer;
+
+ memcpy( p_buffer, "index", 6 );
+ SetDWLE( &p_buffer[6], p_stream->i_serial_no );
+ SetQWLE( &p_buffer[10], p_stream->skeleton.i_index_count ); /* num keypoints */
+ SetQWLE( &p_buffer[18], 1000000 );
+ SetQWLE( &p_buffer[34], p_stream->i_length );
+ memcpy( p_buffer + INDEX_BASE_SIZE, p_stream->skeleton.p_index, p_stream->skeleton.i_index_payload );
+ *pi_size = INDEX_BASE_SIZE + p_stream->skeleton.i_index_size;
+}
+
+static void OggGetSkeletonFisbone( uint8_t **pp_buffer, long *pi_size,
+ sout_input_t *p_input, sout_mux_t *p_mux )
+{
+ uint8_t *psz_header;
+ uint8_t *p_buffer;
+ const char *psz_value = NULL;
+ ogg_stream_t *p_stream = (ogg_stream_t *) p_input->p_sys;
+ struct
+ {
+ char *psz_content_type;
+ char *psz_role;
+ long int i_size;
+ unsigned int i_count;
+ } headers = { NULL, NULL, 0, 0 };
+ *pi_size = 0;
+
+ switch( p_stream->i_fourcc )
+ {
+ case VLC_CODEC_VORBIS:
+ psz_value = "audio/vorbis";
+ break;
+ case VLC_CODEC_THEORA:
+ psz_value = "video/theora";
+ break;
+ case VLC_CODEC_DAALA:
+ psz_value = "video/daala";
+ break;
+ case VLC_CODEC_SPEEX:
+ psz_value = "audio/speex";
+ break;
+ case VLC_CODEC_FLAC:
+ psz_value = "audio/flac";
+ break;
+ case VLC_CODEC_CMML:
+ psz_value = "text/cmml";
+ break;
+ case VLC_CODEC_KATE:
+ psz_value = "application/kate";
+ break;
+ case VLC_CODEC_VP8:
+ psz_value = "video/x-vp8";
+ break;
+ default:
+ psz_value = "application/octet-stream";
+ msg_Warn( p_mux, "Unknown fourcc for stream %s, setting Content-Type to %s",
+ vlc_fourcc_GetDescription( p_stream->i_cat, p_stream->i_fourcc ),
+ psz_value );
+ }
+
+ /* Content Type Header */
+ if ( asprintf( &headers.psz_content_type, "Content-Type: %s\r\n", psz_value ) != -1 )
+ {
+ headers.i_size += strlen( headers.psz_content_type );
+ headers.i_count++;
+ }
+
+ /* Set Role Header */
+ if ( p_input->p_fmt->i_priority > ES_PRIORITY_NOT_SELECTABLE )
+ {
+ int i_max_prio = ES_PRIORITY_MIN;
+ for ( int i=0; i< p_mux->i_nb_inputs; i++ )
+ {
+ if ( p_mux->pp_inputs[i]->p_fmt->i_cat != p_input->p_fmt->i_cat ) continue;
+ i_max_prio = __MAX( p_mux->pp_inputs[i]->p_fmt->i_priority, i_max_prio );
+ }
+
+ psz_value = NULL;
+ if ( p_input->p_fmt->i_cat == AUDIO_ES || p_input->p_fmt->i_cat == VIDEO_ES )
+ {
+ if ( p_input->p_fmt->i_priority == i_max_prio && i_max_prio >= ES_PRIORITY_SELECTABLE_MIN )
+ psz_value = ( p_input->p_fmt->i_cat == VIDEO_ES ) ?
+ "video/main" : "audio/main";
+ else
+ psz_value = ( p_input->p_fmt->i_cat == VIDEO_ES ) ?
+ "video/alternate" : "audio/alternate";
+ }
+ else if ( p_input->p_fmt->i_cat == SPU_ES )
+ {
+ psz_value = ( p_input->p_fmt->i_codec == VLC_CODEC_KATE ) ?
+ "text/karaoke" : "text/subtitle";
+ }
+
+ if ( psz_value && asprintf( &headers.psz_role, "Role: %s\r\n", psz_value ) != -1 )
+ {
+ headers.i_size += strlen( headers.psz_role );
+ headers.i_count++;
+ }
+ }
+
+ *pp_buffer = calloc( FISBONE_BASE_SIZE + headers.i_size, sizeof(uint8_t) );
+ if ( !*pp_buffer ) return;
+ p_buffer = *pp_buffer;
+
+ memcpy( p_buffer, "fisbone", 8 );
+ SetDWLE( &p_buffer[8], FISBONE_BASE_OFFSET ); /* offset to message headers */
+ SetDWLE( &p_buffer[12], p_stream->i_serial_no );
+ SetDWLE( &p_buffer[16], headers.i_count );
+
+ /* granulerate den */
+ switch ( p_input->p_fmt->i_cat )
+ {
+ case VIDEO_ES:
+ SetQWLE( &(*pp_buffer)[20], p_input->p_fmt->video.i_frame_rate );
+ SetQWLE( &(*pp_buffer)[28], p_input->p_fmt->video.i_frame_rate_base );
+ break;
+ case AUDIO_ES:
+ SetQWLE( &(*pp_buffer)[20], p_input->p_fmt->audio.i_rate );
+ SetQWLE( &(*pp_buffer)[28], 1 );
+ break;
+ default:
+ SetQWLE( &(*pp_buffer)[20], 1000 );
+ SetQWLE( &(*pp_buffer)[28], 1 );
+ }
+
+ /* preroll */
+ if ( p_input->p_fmt->p_extra )
+ SetDWLE( &(*pp_buffer)[44], xiph_CountHeaders( p_input->p_fmt->p_extra, p_input->p_fmt->i_extra ) );
+
+ if ( headers.i_size > 0 )
+ {
+ psz_header = *pp_buffer + FISBONE_BASE_SIZE;
+ memcpy( psz_header, headers.psz_content_type, strlen( headers.psz_content_type ) );
+ psz_header += strlen( headers.psz_content_type );
+ if ( headers.psz_role )
+ memcpy( psz_header, headers.psz_role, strlen( headers.psz_role ) );
+ }
+ *pi_size = FISBONE_BASE_SIZE + headers.i_size;
+
+ free( headers.psz_content_type );
+ free( headers.psz_role );
+}
+
+static void OggFillSkeletonFishead( uint8_t *p_buffer, sout_mux_t *p_mux )
+{
+ memcpy( p_buffer, "fishead", 8 );
+ SetWLE( &p_buffer[8], 4 );
+ SetWLE( &p_buffer[10], 0 );
+ SetQWLE( &p_buffer[20], 1000 );
+ SetQWLE( &p_buffer[36], 1000 );
+ SetQWLE( &p_buffer[64], p_mux->p_sys->i_pos - p_mux->p_sys->i_segment_start ); /* segment length */
+ SetQWLE( &p_buffer[72], p_mux->p_sys->i_data_start - p_mux->p_sys->i_segment_start ); /* data start offset */
+}
+
+static int32_t OggFillDsHeader( uint8_t *p_buffer, oggds_header_t *p_oggds_header, int i_cat )
+{
+ int index = 0;
+ p_buffer[index] = p_oggds_header->i_packet_type;
+ index++;
+ memcpy( &p_buffer[index], p_oggds_header->stream_type, sizeof(p_oggds_header->stream_type) );
+ index += sizeof(p_oggds_header->stream_type);
+ memcpy(&p_buffer[index], p_oggds_header->sub_type, sizeof(p_oggds_header->sub_type) );
+ index += sizeof(p_oggds_header->sub_type);
+
+ /* The size is filled at the end */
+ uint8_t *p_isize = &p_buffer[index];
+ index += 4;
+
+ SetQWLE( &p_buffer[index], p_oggds_header->i_time_unit );
+ index += 8;
+ SetQWLE( &p_buffer[index], p_oggds_header->i_samples_per_unit );
+ index += 8;
+ SetDWLE( &p_buffer[index], p_oggds_header->i_default_len );
+ index += 4;
+ SetDWLE( &p_buffer[index], p_oggds_header->i_buffer_size );
+ index += 4;
+ SetWLE( &p_buffer[index], p_oggds_header->i_bits_per_sample );
+ index += 2;
+ SetWLE( &p_buffer[index], p_oggds_header->i_padding_0 );
+ index += 2;
+ /* audio or video */
+ switch( i_cat )
+ {
+ case VIDEO_ES:
+ SetDWLE( &p_buffer[index], p_oggds_header->header.video.i_width );
+ SetDWLE( &p_buffer[index+4], p_oggds_header->header.video.i_height );
+ break;
+ case AUDIO_ES:
+ SetWLE( &p_buffer[index], p_oggds_header->header.audio.i_channels );
+ SetWLE( &p_buffer[index+2], p_oggds_header->header.audio.i_block_align );
+ SetDWLE( &p_buffer[index+4], p_oggds_header->header.audio.i_avgbytespersec );
+ break;
+ }
+ index += 8;
+ SetDWLE( &p_buffer[index], p_oggds_header->i_padding_1 );
+ index += 4;
+
+ /* extra header */
+ if( p_oggds_header->i_size > 0 )
+ {
+ memcpy( &p_buffer[index], (uint8_t *) p_oggds_header + sizeof(*p_oggds_header), p_oggds_header->i_size );
+ index += p_oggds_header->i_size;
+ }
+
+ SetDWLE( p_isize, index-1 );
+ return index;
+}
+
+static void OggFillVP8Header( uint8_t *p_buffer, sout_input_t *p_input )
+{
+ memcpy( p_buffer, "OVP80\x01\x01\x00", 8 );
+ SetWBE( &p_buffer[8], p_input->p_fmt->video.i_width );
+ SetDWBE( &p_buffer[14], p_input->p_fmt->video.i_sar_den );/* 24 bits, 15~ */
+ SetDWBE( &p_buffer[11], p_input->p_fmt->video.i_sar_num );/* 24 bits, 12~ */
+ SetWBE( &p_buffer[10], p_input->p_fmt->video.i_height );
+ SetDWBE( &p_buffer[18], p_input->p_fmt->video.i_frame_rate );
+ SetDWBE( &p_buffer[22], p_input->p_fmt->video.i_frame_rate_base );
+}
+
+static bool OggCreateHeaders( sout_mux_t *p_mux )