+static int Demux( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ block_t *p_block_in, *p_block_out;
+
+ if( !( p_block_in = stream_Block( p_demux->s, FLAC_PACKET_SIZE ) ) )
+ return 0;
+
+ p_block_in->i_pts = p_block_in->i_dts = p_sys->b_start ? 1 : 0;
+ p_sys->b_start = false;
+
+ while( (p_block_out = p_sys->p_packetizer->pf_packetize(
+ p_sys->p_packetizer, &p_block_in )) )
+ {
+ while( p_block_out )
+ {
+ block_t *p_next = p_block_out->p_next;
+
+ p_block_out->p_next = NULL;
+
+ if( p_sys->p_es == NULL )
+ {
+ p_sys->p_packetizer->fmt_out.b_packetized = true;
+ p_sys->p_packetizer->fmt_out.audio_replay_gain = p_sys->replay_gain;
+ p_sys->p_es = es_out_Add( p_demux->out, &p_sys->p_packetizer->fmt_out);
+ }
+
+ /* set PCR */
+ if( p_block_out->i_dts >= p_sys->i_pts_start )
+ es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block_out->i_dts );
+ else
+ es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
+
+ p_sys->i_pts = p_block_out->i_dts;
+ es_out_Send( p_demux->out, p_sys->p_es, p_block_out );
+
+ p_block_out = p_next;
+ }
+ }
+ return 1;
+}
+
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int64_t ControlGetLength( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ const int64_t i_size = stream_Size(p_demux->s) - p_sys->i_data_pos;
+ int64_t i_length = p_sys->i_length;
+ int i;
+
+ /* Try to fix length using seekpoint and current size for truncated file */
+ for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
+ {
+ seekpoint_t *s = p_sys->seekpoint[i];
+ if( s->i_byte_offset <= i_size )
+ {
+ if( i+1 < p_sys->i_seekpoint )
+ {
+ /* Broken file */
+ seekpoint_t *n = p_sys->seekpoint[i+1];
+ assert( n->i_byte_offset != s->i_byte_offset); /* Should be ensured by ParseSeekTable */
+ i_length = s->i_time_offset + (n->i_time_offset-s->i_time_offset) * (i_size-s->i_byte_offset) / (n->i_byte_offset-s->i_byte_offset);
+ }
+ break;
+ }
+ }
+ return i_length;
+}
+
+static int64_t ControlGetTime( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ return __MAX(p_sys->i_pts, p_sys->i_pts_start) + p_sys->i_time_offset;
+}
+
+static int ControlSetTime( demux_t *p_demux, int64_t i_time )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ int64_t i_next_time;
+ int64_t i_next_offset;
+ int64_t i_delta_time;
+ int64_t i_delta_offset;
+ bool b_seekable;
+ int i;
+
+ /* */
+ stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable );
+ if( !b_seekable )
+ return VLC_EGENERIC;
+
+ /* */
+ assert( p_sys->i_seekpoint > 0 ); /* ReadMeta ensure at least (0,0) */
+ for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
+ {
+ if( p_sys->seekpoint[i]->i_time_offset <= i_time )
+ break;
+ }
+ if( i+1 < p_sys->i_seekpoint )
+ {
+ i_next_time = p_sys->seekpoint[i+1]->i_time_offset;
+ i_next_offset = p_sys->seekpoint[i+1]->i_byte_offset;
+ }
+ else
+ {
+ i_next_time = p_sys->i_length;
+ i_next_offset = stream_Size(p_demux->s)-p_sys->i_data_pos;
+ }
+ i_delta_time = i_time - p_sys->seekpoint[i]->i_time_offset;
+ i_delta_offset = (i_next_offset - p_sys->seekpoint[i]->i_byte_offset) * i_delta_time /
+ (p_sys->seekpoint[i+1]->i_time_offset-p_sys->seekpoint[i]->i_time_offset);
+
+ /* XXX We do exact seek if it's not too far away(45s) */
+ if( i_delta_time < 45*INT64_C(1000000) )
+ {
+ if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos ) )
+ return VLC_EGENERIC;
+ p_sys->i_time_offset = p_sys->seekpoint[i]->i_time_offset - p_sys->i_pts;
+ p_sys->i_pts_start = p_sys->i_pts+i_delta_time;
+ es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, p_sys->p_es, p_sys->i_pts_start );
+ }
+ else
+ {
+ if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos + i_delta_offset ) )
+ return VLC_EGENERIC;
+ p_sys->i_pts_start = p_sys->i_pts;
+ p_sys->i_time_offset = (p_sys->seekpoint[i]->i_time_offset+i_delta_time) - p_sys->i_pts;
+ }
+ return VLC_SUCCESS;
+}
+
+static int Control( demux_t *p_demux, int i_query, va_list args )