*****************************************************************************/
static int Control ( sout_mux_t *, int, va_list );
static int AddStream( sout_mux_t *, sout_input_t * );
-static int DelStream( sout_mux_t *, sout_input_t * );
+static void DelStream( sout_mux_t *, sout_input_t * );
static int Mux ( sout_mux_t * );
static int MuxBlock ( sout_mux_t *, sout_input_t * );
mtime_t i_length;
int i_packet_no;
int i_serial_no;
- int i_keyframe_granule_shift; /* Theora only */
+ int i_keyframe_granule_shift; /* Theora and Daala only */
int i_last_keyframe; /* dirac and theora */
int i_num_frames; /* Theora only */
uint64_t u_last_granulepos; /* Used for correct EOS page */
uint64_t i_last_keyframe_pos;
uint64_t i_last_keyframe_time;
} skeleton;
+
+ int i_dirac_last_pt;
+ int i_dirac_last_dt;
+ mtime_t i_baseptsdelay;
+
} ogg_stream_t;
struct sout_mux_sys_t
var_InheritInteger( p_this, SOUT_CFG_PREFIX "indexintvl" );
p_sys->skeleton.i_index_ratio =
var_InheritFloat( p_this, SOUT_CFG_PREFIX "indexratio" );
+ p_sys->i_data_start = 0;
+ p_sys->i_segment_start = 0;
p_mux->p_sys = p_sys;
p_mux->pf_control = Control;
p_mux->pf_addstream = AddStream;
{
OggCreateStreamFooter( p_mux, p_sys->pp_del_streams[i] );
free( p_sys->pp_del_streams[i]->p_oggds_header );
- free( p_sys->pp_del_streams[i] );
free( p_sys->pp_del_streams[i]->skeleton.p_index );
+ free( p_sys->pp_del_streams[i] );
}
free( p_sys->pp_del_streams );
p_sys->i_streams -= p_sys->i_del_streams;
p_stream->p_oggds_header = 0;
+ p_stream->i_baseptsdelay = -1;
+ p_stream->i_dirac_last_pt = -1;
+ p_stream->i_dirac_last_dt = -1;
+
switch( p_input->p_fmt->i_cat )
{
case VIDEO_ES:
!p_input->p_fmt->video.i_frame_rate_base )
{
msg_Warn( p_mux, "Missing frame rate, assuming 25fps" );
- p_input->p_fmt->video.i_frame_rate = 25;
- p_input->p_fmt->video.i_frame_rate_base = 1;
+ assert(p_input->p_fmt == &p_input->fmt);
+ p_input->fmt.video.i_frame_rate = 25;
+ p_input->fmt.video.i_frame_rate_base = 1;
}
switch( p_stream->i_fourcc )
{
case VLC_CODEC_MP4V:
case VLC_CODEC_MPGV:
+ case VLC_CODEC_MP1V:
+ case VLC_CODEC_MP2V:
case VLC_CODEC_DIV3:
case VLC_CODEC_MJPG:
case VLC_CODEC_WMV1:
msg_Dbg( p_mux, "theora stream" );
break;
+ case VLC_CODEC_DAALA:
+ msg_Dbg( p_mux, "daala stream" );
+ break;
+
+ case VLC_CODEC_VP8:
+ msg_Dbg( p_mux, "VP8 stream" );
+ break;
+
default:
FREENULL( p_input->p_sys );
return VLC_EGENERIC;
/*****************************************************************************
* DelStream: Delete an elementary stream from the muxed stream
*****************************************************************************/
-static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
+static void DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
{
sout_mux_sys_t *p_sys = p_mux->p_sys;
ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
}
p_input->p_sys = NULL;
-
- return 0;
}
/*****************************************************************************
/* update diff points */
p_stream->skeleton.i_last_keyframe_pos = p_sys->i_pos;
p_stream->skeleton.i_last_keyframe_time = i_time;
- msg_Dbg( p_mux, "Added index on stream %d entry %"PRId64" %"PRId64,
+ msg_Dbg( p_mux, "Added index on stream %d entry %zd %"PRIu64,
p_stream->i_serial_no, p_sys->i_pos - p_sys->i_segment_start, i_time );
return true;
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_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, "Unkown fourcc for stream %s, setting Content-Type to %s",
+ 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 );
}
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 ) );
- 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 ) );
-
+ 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 );
/* extra header */
if( p_oggds_header->i_size > 0 )
{
- memcpy( &p_buffer[index], p_oggds_header + sizeof(*p_oggds_header), p_oggds_header->i_size );
+ memcpy( &p_buffer[index], (uint8_t *) p_oggds_header + sizeof(*p_oggds_header), p_oggds_header->i_size );
index += p_oggds_header->i_size;
}
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 )
{
block_t *p_hdr = NULL;
sout_input_t *p_input = p_mux->pp_inputs[i];
p_stream = (ogg_stream_t*)p_input->p_sys;
- bool video = ( p_stream->i_fourcc == VLC_CODEC_THEORA || p_stream->i_fourcc == VLC_CODEC_DIRAC );
+ bool video = ( p_stream->i_fourcc == VLC_CODEC_THEORA ||
+ p_stream->i_fourcc == VLC_CODEC_DIRAC ||
+ p_stream->i_fourcc == VLC_CODEC_DAALA );
if( ( ( pass == 0 && !video ) || ( pass == 1 && video ) ) )
continue;
if( p_stream->i_fourcc == VLC_CODEC_VORBIS ||
p_stream->i_fourcc == VLC_CODEC_SPEEX ||
p_stream->i_fourcc == VLC_CODEC_OPUS ||
- p_stream->i_fourcc == VLC_CODEC_THEORA )
+ p_stream->i_fourcc == VLC_CODEC_THEORA ||
+ p_stream->i_fourcc == VLC_CODEC_DAALA )
{
- /* First packet in order: vorbis/speex/theora info */
+ /* First packet in order: vorbis/speex/opus/theora/daala info */
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
ogg_stream_packetin( &p_stream->os, &op );
p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
- /* Get keyframe_granule_shift for theora granulepos calculation */
- if( p_stream->i_fourcc == VLC_CODEC_THEORA )
+ /* Get keyframe_granule_shift for theora or daala granulepos calculation */
+ if( p_stream->i_fourcc == VLC_CODEC_THEORA ||
+ p_stream->i_fourcc == VLC_CODEC_DAALA )
{
p_stream->i_keyframe_granule_shift =
( (op.packet[40] & 0x03) << 3 ) | ( (op.packet[41] & 0xe0) >> 5 );
ogg_stream_packetin( &p_stream->os, &op );
p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
}
+ else if( p_stream->i_fourcc == VLC_CODEC_VP8 )
+ {
+ /* VP8 Header */
+ op.packet = malloc( 26 );
+ if( !op.packet )
+ return false;
+ op.bytes = 26;
+ OggFillVP8Header( op.packet, p_input );
+ op.b_o_s = 1;
+ op.e_o_s = 0;
+ op.granulepos = 0;
+ op.packetno = p_stream->i_packet_no++;
+ ogg_stream_packetin( &p_stream->os, &op );
+ p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
+ free( op.packet );
+ }
else if( p_stream->p_oggds_header )
{
/* ds header */
}
/* Write previous headers */
+ for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
+ {
+ /* flag headers to be resent for streaming clients */
+ p_og->i_flags |= BLOCK_FLAG_HEADER;
+ }
p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_hdr );
p_hdr = NULL;
if( p_stream->i_fourcc == VLC_CODEC_VORBIS ||
p_stream->i_fourcc == VLC_CODEC_SPEEX ||
p_stream->i_fourcc == VLC_CODEC_OPUS ||
- p_stream->i_fourcc == VLC_CODEC_THEORA )
+ p_stream->i_fourcc == VLC_CODEC_THEORA ||
+ p_stream->i_fourcc == VLC_CODEC_DAALA )
{
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
}
/* set HEADER flag */
+ /* flag headers to be resent for streaming clients */
for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
{
p_og->i_flags |= BLOCK_FLAG_HEADER;
msg_Dbg( p_mux, "No stream length, using default allocation for index" );
}
i_size *= ( 8.0 / 7 ); /* 7bits encoding overhead */
- msg_Dbg( p_mux, "allocating %"PRId64" bytes for index", i_size );
+ msg_Dbg( p_mux, "allocating %zu bytes for index", i_size );
p_stream->skeleton.p_index = calloc( i_size, sizeof(uint8_t) );
if ( !p_stream->skeleton.p_index ) return false;
p_stream->skeleton.i_index_size = i_size;
/* If we're switching to end of headers, then that's data start */
if ( p_sys->b_can_add_streams )
{
- msg_Dbg( p_mux, "data starts from %"PRId64, p_sys->i_pos );
+ msg_Dbg( p_mux, "data starts from %zu", p_sys->i_pos );
p_sys->i_data_start = p_sys->i_pos;
}
p_stream->i_fourcc != VLC_CODEC_SPEEX &&
p_stream->i_fourcc != VLC_CODEC_OPUS &&
p_stream->i_fourcc != VLC_CODEC_THEORA &&
+ p_stream->i_fourcc != VLC_CODEC_DAALA &&
+ p_stream->i_fourcc != VLC_CODEC_VP8 &&
p_stream->i_fourcc != VLC_CODEC_DIRAC )
{
p_data = block_Realloc( p_data, 1, p_data->i_buffer );
p_data->p_buffer[0] = PACKET_IS_SYNCPOINT; // FIXME
}
+ if ( p_stream->i_fourcc == VLC_CODEC_DIRAC && p_stream->i_baseptsdelay < 0 )
+ p_stream->i_baseptsdelay = p_data->i_pts - p_data->i_dts;
+
op.packet = p_data->p_buffer;
op.bytes = p_data->i_buffer;
op.b_o_s = 0;
/* number of sample from begining + current packet */
op.granulepos =
( p_data->i_dts - p_sys->i_start_dts + p_data->i_length ) *
- (mtime_t)p_input->p_fmt->audio.i_rate / INT64_C(1000000);
+ (mtime_t)p_input->p_fmt->audio.i_rate / CLOCK_FREQ;
i_time = p_data->i_dts - p_sys->i_start_dts;
AddIndexEntry( p_mux, i_time, p_input );
{
/* number of sample from begining */
op.granulepos = ( p_data->i_dts - p_sys->i_start_dts ) *
- p_stream->p_oggds_header->i_samples_per_unit / INT64_C(1000000);
+ p_stream->p_oggds_header->i_samples_per_unit / CLOCK_FREQ;
}
}
else if( p_stream->i_cat == VIDEO_ES )
{
- if( p_stream->i_fourcc == VLC_CODEC_THEORA )
+ if( p_stream->i_fourcc == VLC_CODEC_THEORA ||
+ p_stream->i_fourcc == VLC_CODEC_DAALA )
{
p_stream->i_num_frames++;
if( p_data->i_flags & BLOCK_FLAG_TYPE_I )
p_stream->i_last_keyframe = p_stream->i_num_frames;
/* presentation time */
- i_time = INT64_C(1000000) * ( p_stream->i_num_frames - 1 ) *
+ i_time = CLOCK_FREQ * ( p_stream->i_num_frames - 1 ) *
p_input->p_fmt->video.i_frame_rate_base / p_input->p_fmt->video.i_frame_rate;
AddIndexEntry( p_mux, i_time, p_input );
}
}
else if( p_stream->i_fourcc == VLC_CODEC_DIRAC )
{
- mtime_t dt = (p_data->i_dts - p_sys->i_start_dts + 1)
- * p_input->p_fmt->video.i_frame_rate *2
- / p_input->p_fmt->video.i_frame_rate_base
- / INT64_C(1000000);
- mtime_t delay = (p_data->i_pts - p_data->i_dts + 1)
- * p_input->p_fmt->video.i_frame_rate *2
- / p_input->p_fmt->video.i_frame_rate_base
- / INT64_C(1000000);
+
+#define FRAME_ROUND(a) \
+ if ( ( a + 5000 / CLOCK_FREQ ) > ( a / CLOCK_FREQ ) )\
+ a += 5000;\
+ a /= CLOCK_FREQ;
+
+ mtime_t dt = (p_data->i_dts - p_sys->i_start_dts) * p_input->p_fmt->video.i_frame_rate / p_input->p_fmt->video.i_frame_rate_base;
+ FRAME_ROUND( dt );
+
+ mtime_t pt = (p_data->i_pts - p_sys->i_start_dts - p_stream->i_baseptsdelay ) * p_input->p_fmt->video.i_frame_rate / p_input->p_fmt->video.i_frame_rate_base;
+ FRAME_ROUND( pt );
+
+ /* (shro) some PTS could be repeated within 1st frames */
+ if ( pt == p_stream->i_dirac_last_pt )
+ pt++;
+ else
+ p_stream->i_dirac_last_pt = pt;
+
+ /* (shro) some DTS could be repeated within 1st frames */
+ if ( dt == p_stream->i_dirac_last_dt )
+ dt++;
+ else
+ p_stream->i_dirac_last_dt = dt;
+
if( p_data->i_flags & BLOCK_FLAG_TYPE_I )
p_stream->i_last_keyframe = dt;
mtime_t dist = dt - p_stream->i_last_keyframe;
- op.granulepos = dt << 31 | (dist&0xff00) << 14
+
+ /* Everything increments by two for progressive */
+ if ( true )
+ {
+ pt *=2;
+ dt *=2;
+ }
+
+ mtime_t delay = pt - dt;
+ if ( delay < 0 ) delay *= -1;
+
+ op.granulepos = (pt - delay) << 31 | (dist&0xff00) << 14
| (delay&0x1fff) << 9 | (dist&0xff);
+#ifndef NDEBUG
+ msg_Dbg( p_mux, "dts %"PRId64" pts %"PRId64" dt %"PRId64" pt %"PRId64" delay %"PRId64" granule %"PRId64,
+ (p_data->i_dts - p_sys->i_start_dts),
+ (p_data->i_pts - p_sys->i_start_dts ),
+ dt, pt, delay, op.granulepos );
+#endif
+
AddIndexEntry( p_mux, dt, p_input );
}
+ else if( p_stream->i_fourcc == VLC_CODEC_VP8 )
+ {
+ p_stream->i_num_frames++;
+ if( p_data->i_flags & BLOCK_FLAG_TYPE_I )
+ {
+ p_stream->i_num_keyframes++;
+ p_stream->i_last_keyframe = p_stream->i_num_frames;
+
+ /* presentation time */
+ i_time = CLOCK_FREQ * ( p_stream->i_num_frames - 1 ) *
+ p_input->p_fmt->video.i_frame_rate_base / p_input->p_fmt->video.i_frame_rate;
+ AddIndexEntry( p_mux, i_time, p_input );
+ }
+ op.granulepos = ( ((int64_t)p_stream->i_num_frames) << 32 ) |
+ ( ( ( p_stream->i_num_frames - p_stream->i_last_keyframe ) & 0x07FFFFFF ) << 3 );
+ }
else if( p_stream->p_oggds_header )
op.granulepos = ( p_data->i_dts - p_sys->i_start_dts ) * INT64_C(10) /
p_stream->p_oggds_header->i_time_unit;
OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
p_stream->i_dts = -1;
p_stream->i_length = 0;
-
p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_og );
}
else