static int Control (demux_t *demux, int query, va_list args) { demux_sys_t *sys = demux->p_sys; switch (query) { case DEMUX_GET_POSITION: *va_arg (args, float *) = 0.f; break; case DEMUX_GET_LENGTH: *va_arg (args, int64_t *) = INT64_C(0); break; case DEMUX_GET_TIME: *va_arg (args, int64_t *) = date_Get (&sys->date); break; case DEMUX_SET_TIME: date_Set (&sys->date, va_arg (args, int64_t)); break; case DEMUX_SET_NEXT_DEMUX_TIME: { const mtime_t pts = va_arg (args, int64_t ); if (sys->next_time == VLC_TS_INVALID) /* first invocation? */ { date_Set (&sys->date, pts); date_Decrement (&sys->date, 1); } sys->next_time = pts; break; } case DEMUX_GET_PTS_DELAY: { int64_t *v = va_arg (args, int64_t *); *v = INT64_C(1000) * var_InheritInteger (demux, "live-caching"); break; } case DEMUX_CAN_PAUSE: case DEMUX_CAN_CONTROL_PACE: case DEMUX_CAN_SEEK: *va_arg (args, bool *) = true; break; default: return VLC_EGENERIC; } return VLC_SUCCESS; }
/* backdate the list [p_block .. p_block->p_next where p_next == p_last] */ static void dirac_BackdateDTS( block_t *p_block, block_t *p_last, date_t *p_dts ) { /* Transverse p_last backwards. (no p_prev anymore) */ block_t **pp_array = NULL; int n = block_ChainToArray( p_block, &pp_array ); while( n ) if( pp_array[--n] == p_last ) break; /* want to start at p_last->p_prev */ while( n-- ) { if( pp_array[n]->i_flags & DIRAC_NON_DATED ) continue; if( pp_array[n]->i_dts <= VLC_TS_INVALID ) pp_array[n]->i_dts = date_Decrement( p_dts, 1 ); } free( pp_array ); }
/* backdate the list [p_block .. p_block->p_next where p_next == p_last] */ static void dirac_BackdatePTS( block_t *p_block, block_t *p_last, date_t *p_pts, uint32_t u_pts_picnum ) { /* Transverse p_last backwards. (no p_prev anymore) */ block_t **pp_array = NULL; int n = block_ChainToArray( p_block, &pp_array ); while( n ) if( pp_array[--n] == p_last ) break; /* want to start at p_last->p_prev */ while( n-- ) { if( pp_array[n]->i_flags & DIRAC_NON_DATED ) continue; if( pp_array[n]->i_dts > VLC_TS_INVALID ) continue; dirac_block_encap_t *dbe = dirac_GetBlockEncap( pp_array[n] ); int32_t u_pic_num = dbe ? dbe->u_picture_number : 0; int32_t i_dist = u_pic_num - u_pts_picnum; date_t pts = *p_pts; if( i_dist >= 0 ) pp_array[n]->i_pts = date_Increment( &pts, i_dist ); else pp_array[n]->i_pts = date_Decrement( &pts, -i_dist ); } free( pp_array ); }
/** * dirac_TimeGenPush: * @p_dec: vlc object * @p_block_in: whole encapsulation unit to generate timestamps for * * Returns: * 0: everything ok * 1: EOS occurred, please flush and reset * 2: picture number discontinuity, please flush and reset */ static int dirac_TimeGenPush( decoder_t *p_dec, block_t *p_block_in ) { decoder_sys_t *p_sys = p_dec->p_sys; dirac_block_encap_t *p_dbe; if( p_block_in->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) { /* NB, this test occurs after the timegen push, so as to * push the block into the output queue */ return 1; } if( p_block_in->i_flags & DIRAC_NON_DATED ) { /* no picture found, which means p_block_in is a non-dated EU, * do not try and put a date on it */ return 0; } p_dbe = dirac_GetBlockEncap( p_block_in ); uint32_t u_picnum = p_dbe ? p_dbe->u_picture_number : 0; /* * Simple DTS regeneration: * - DTS values linearly increase in stream order. * - Every time a DTS occurs at the input, sync to it * - If this is the first DTS seen, backdate all the previous ones that are undated * - If a DTS is missing, guess that it increases by one picture period * - If never seen DTS, don't do anything */ /* * Simple PTS regeneration * - PTS values do not linearly increase in stream order. * - Every time a PTS occurs at the input, sync to it and record picture number * - If a PTS is missing, guess that it differs by the product of picture * period and difference between picture number of sync point and current picture * - If this is the first PTS seen, backdate all previous ones that are undated * - If never seen PTS, don't do anything */ /* * Stage 1, sync to input timestamps, backdate timestamps for old * EUs that are in the outqueue with missing dates */ if( p_block_in->i_dts > VLC_TS_INVALID ) do { /* if timestamps exist, sync to them */ if( p_sys->b_dts ) break; /* first dts seen, backdate any packets in outqueue */ p_sys->b_dts = true; date_t dts = p_sys->dts; dirac_BackdateDTS( p_sys->p_outqueue, p_block_in, &dts ); } while( 0 ); if( p_block_in->i_pts > VLC_TS_INVALID ) do { /* if timestamps exist, sync to them */ p_sys->u_pts_picnum = u_picnum; p_sys->i_pts = p_block_in->i_pts; if( p_sys->b_pts ) break; /* first pts seen, backdate any packets in outqueue */ p_sys->b_pts = true; date_t pts = p_sys->dts; date_Set( &pts, p_sys->i_pts ); dirac_BackdatePTS( p_sys->p_outqueue, p_block_in, &pts, p_sys->u_pts_picnum ); } while( 0 ); /* * Stage 2, don't attempt to forwards interpolate timestamps for * blocks if the picture rates aren't known */ if( !p_sys->b_seen_seq_hdr ) { return 0; } /* * Stage 3, for block_in, interpolate any missing timestamps */ if( p_sys->b_dts && p_block_in->i_dts <= VLC_TS_INVALID ) { /* dts has previously been seen, but not this time, interpolate */ p_block_in->i_dts = date_Increment( &p_sys->dts, 1 ); } if( p_sys->b_pts && p_block_in->i_pts <= VLC_TS_INVALID ) { /* pts has previously been seen, but not this time, interpolate */ date_t pts = p_sys->dts; date_Set( &pts, p_sys->i_pts ); int32_t i_dist = u_picnum - p_sys->u_pts_picnum; if( i_dist >= 0 ) p_block_in->i_pts = date_Increment( &pts, i_dist ); else p_block_in->i_pts = date_Decrement( &pts, -i_dist ); } /* If pts and dts have been seen, there is no need to simulate operation * of the decoder reorder buffer */ /* If neither have been seen, there is little point in simulating */ if( p_sys->b_dts == p_sys->b_pts ) return 0; if( !p_sys->p_out_dts ) p_sys->p_out_dts = p_sys->p_outqueue; /* model the reorder buffer */ block_t *p_block = dirac_Reorder( p_dec, p_block_in, u_picnum ); if( !p_block ) return 0; /* A future ehancement is to stop modeling the reorder buffer as soon as * the first packet is output -- interpolate the past and freewheel for * the future */ p_dbe = dirac_GetBlockEncap( p_block ); u_picnum = p_dbe ? p_dbe->u_picture_number : 0; if( p_sys->b_tg_last_picnum ) { if( dirac_PictureNbeforeM( u_picnum, p_sys->u_tg_last_picnum ) ) { msg_Warn( p_dec, "stream jumped? %d < %d: resetting" , u_picnum, p_sys->u_tg_last_picnum ); /* pictures only emerge from the reorder buffer in sequence * if a stream suddenly jumped backwards without a signaling * a discontinuity, some pictures will get stuck in the RoB. * flush the RoB. */ /* this could be a bit less indiscriminate */ p_dbe = dirac_GetBlockEncap( p_sys->p_outqueue ); uint32_t u_prev_parse_offset = p_dbe ? p_dbe->u_last_next_offset : 0; block_ChainRelease( p_sys->p_outqueue ); p_sys->p_outqueue = dirac_EmitEOS( p_dec, u_prev_parse_offset ); if( p_sys->p_outqueue ) p_sys->p_outqueue->i_flags = BLOCK_FLAG_DISCONTINUITY | DIRAC_NON_DATED; /* return 2, so as not to reset the b_dts flags -- needed if * using the rawdirac demuxer with broken stream */ return 2; } } p_sys->b_tg_last_picnum = true; p_sys->u_tg_last_picnum = u_picnum; if( !p_sys->b_pts ) { /* some demuxers (eg, AVI) will provide a series of fake dts values, * which are actually inorder pts values (ie, what should be seen at * the output of a decoder. A main reason for simulating the reorder * buffer is to turn the inorder fakedts into an out-of-order pts */ p_block->i_pts = p_sys->p_out_dts->i_dts; p_sys->p_out_dts->i_dts = VLC_TS_INVALID; } /* If pts was copied from dts, the dts needs to be corrected to account for reordering*/ /* If dts has never been seen, the same needs to happen */ p_sys->p_out_dts->i_dts = p_block->i_pts - p_sys->i_pts_offset; /* move dts pointer */ if( p_sys->p_out_dts ) p_sys->p_out_dts = p_sys->p_out_dts->p_next; return 0; }
static block_t *OutputPicture( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_pic = NULL; block_t **pp_pic_last = &p_pic; if( unlikely(!p_sys->p_frame) ) { assert( p_sys->p_frame ); return NULL; } /* Bind matched/referred PPS and SPS */ const h264_picture_parameter_set_t *p_pps = p_sys->p_active_pps; const h264_sequence_parameter_set_t *p_sps = p_sys->p_active_sps; if( !p_pps || !p_sps ) { DropStoredNAL( p_sys ); return NULL; } if( !p_sys->b_recovered && p_sys->i_recoveryfnum == UINT_MAX && p_sys->i_recovery_frame_cnt == UINT_MAX && p_sys->slice.type == H264_SLICE_TYPE_I ) { /* No way to recover using SEI, just sync on I Slice */ p_sys->b_recovered = true; } bool b_need_sps_pps = p_sys->slice.type == H264_SLICE_TYPE_I && p_sys->p_active_pps && p_sys->p_active_sps; /* Handle SEI recovery */ if ( !p_sys->b_recovered && p_sys->i_recovery_frame_cnt != UINT_MAX && p_sys->i_recoveryfnum == UINT_MAX ) { p_sys->i_recoveryfnum = p_sys->slice.i_frame_num + p_sys->i_recovery_frame_cnt; b_need_sps_pps = true; /* SPS/PPS must be inserted for SEI recovery */ msg_Dbg( p_dec, "Recovering using SEI, prerolling %u reference pics", p_sys->i_recovery_frame_cnt ); } if( p_sys->i_recoveryfnum != UINT_MAX ) { assert(p_sys->b_recovered == false); const unsigned maxFrameNum = 1 << (p_sps->i_log2_max_frame_num + 4); if( (p_sys->i_recoveryfnum > maxFrameNum && (unsigned)p_sys->slice.i_frame_num <= maxFrameNum / 2 && (unsigned)p_sys->slice.i_frame_num >= p_sys->i_recoveryfnum % maxFrameNum ) || (unsigned)p_sys->slice.i_frame_num >= p_sys->i_recoveryfnum ) { p_sys->i_recoveryfnum = UINT_MAX; p_sys->b_recovered = true; msg_Dbg( p_dec, "Recovery from SEI recovery point complete" ); } } /* Gather PPS/SPS if required */ block_t *p_xpsnal = NULL; block_t **pp_xpsnal_tail = &p_xpsnal; if( b_need_sps_pps || p_sys->b_new_sps || p_sys->b_new_pps ) { for( int i = 0; i <= H264_SPS_ID_MAX && (b_need_sps_pps || p_sys->b_new_sps); i++ ) { if( p_sys->sps[i].p_block ) block_ChainLastAppend( &pp_xpsnal_tail, block_Duplicate( p_sys->sps[i].p_block ) ); } for( int i = 0; i < H264_PPS_ID_MAX && (b_need_sps_pps || p_sys->b_new_pps); i++ ) { if( p_sys->pps[i].p_block ) block_ChainLastAppend( &pp_xpsnal_tail, block_Duplicate( p_sys->pps[i].p_block ) ); } } /* Now rebuild NAL Sequence, inserting PPS/SPS if any */ if( p_sys->p_frame->i_flags & BLOCK_FLAG_PRIVATE_AUD ) { block_t *p_au = p_sys->p_frame; p_sys->p_frame = p_au->p_next; p_au->p_next = NULL; p_au->i_flags &= ~BLOCK_FLAG_PRIVATE_AUD; block_ChainLastAppend( &pp_pic_last, p_au ); } if( p_xpsnal ) block_ChainLastAppend( &pp_pic_last, p_xpsnal ); if( p_sys->p_sei ) block_ChainLastAppend( &pp_pic_last, p_sys->p_sei ); assert( p_sys->p_frame ); if( p_sys->p_frame ) block_ChainLastAppend( &pp_pic_last, p_sys->p_frame ); /* Reset chains, now empty */ p_sys->p_frame = NULL; p_sys->pp_frame_last = &p_sys->p_frame; p_sys->p_sei = NULL; p_sys->pp_sei_last = &p_sys->p_sei; p_pic = block_ChainGather( p_pic ); if( !p_pic ) return NULL; /* for PTS Fixup, interlaced fields (multiple AU/block) */ int tFOC = 0, bFOC = 0, PictureOrderCount = 0; h264_compute_poc( p_sps, &p_sys->slice, &p_sys->pocctx, &PictureOrderCount, &tFOC, &bFOC ); unsigned i_num_clock_ts = h264_get_num_ts( p_sps, &p_sys->slice, p_sys->i_pic_struct, tFOC, bFOC ); if( p_sps->frame_mbs_only_flag == 0 && p_sps->vui.b_pic_struct_present_flag ) { switch( p_sys->i_pic_struct ) { /* Top and Bottom field slices */ case 1: case 2: p_pic->i_flags |= BLOCK_FLAG_SINGLE_FIELD; p_pic->i_flags |= (!p_sys->slice.i_bottom_field_flag) ? BLOCK_FLAG_TOP_FIELD_FIRST : BLOCK_FLAG_BOTTOM_FIELD_FIRST; break; /* Each of the following slices contains multiple fields */ case 3: p_pic->i_flags |= BLOCK_FLAG_TOP_FIELD_FIRST; break; case 4: p_pic->i_flags |= BLOCK_FLAG_BOTTOM_FIELD_FIRST; break; case 5: p_pic->i_flags |= BLOCK_FLAG_TOP_FIELD_FIRST; break; case 6: p_pic->i_flags |= BLOCK_FLAG_BOTTOM_FIELD_FIRST; break; default: break; } } /* set dts/pts to current block timestamps */ p_pic->i_dts = p_sys->i_frame_dts; p_pic->i_pts = p_sys->i_frame_pts; /* Fixup missing timestamps after split (multiple AU/block)*/ if( p_pic->i_dts <= VLC_TS_INVALID ) p_pic->i_dts = date_Get( &p_sys->dts ); if( p_sys->slice.type == H264_SLICE_TYPE_I ) p_sys->prevdatedpoc.pts = VLC_TS_INVALID; if( p_pic->i_pts == VLC_TS_INVALID ) { if( p_sys->prevdatedpoc.pts > VLC_TS_INVALID && date_Get( &p_sys->dts ) != VLC_TS_INVALID ) { date_t pts = p_sys->dts; date_Set( &pts, p_sys->prevdatedpoc.pts ); int diff = tFOC - p_sys->prevdatedpoc.num; if( diff > 0 ) date_Increment( &pts, diff ); else date_Decrement( &pts, -diff ); p_pic->i_pts = date_Get( &pts ); } /* In case there's no PTS at all */ else if( p_sys->slice.i_nal_ref_idc == 0 && p_sys->slice.type == H264_SLICE_TYPE_B ) { p_pic->i_pts = p_pic->i_dts; } else if( p_sys->slice.type == H264_SLICE_TYPE_I && date_Get( &p_sys->dts ) != VLC_TS_INVALID ) { /* Hell no PTS on IDR. We're totally blind */ date_t pts = p_sys->dts; date_Increment( &pts, 2 ); p_pic->i_pts = date_Get( &pts ); } } if( p_pic->i_pts > VLC_TS_INVALID ) { p_sys->prevdatedpoc.pts = p_pic->i_pts; p_sys->prevdatedpoc.num = PictureOrderCount; } if( p_pic->i_length == 0 ) { if( p_sps->vui.i_time_scale ) { p_pic->i_length = CLOCK_FREQ * i_num_clock_ts * p_sps->vui.i_num_units_in_tick / p_sps->vui.i_time_scale; } else { date_t next = p_sys->dts; date_Increment( &next, i_num_clock_ts ); p_pic->i_length = date_Get( &next ) - date_Get( &p_sys->dts ); } } #if 0 msg_Err(p_dec, "F/BOC %d/%d POC %d %d rec %d flags %x ref%d fn %d fp %d %d pts %ld len %ld", tFOC, bFOC, PictureOrderCount, p_sys->slice.type, p_sys->b_recovered, p_pic->i_flags, p_sys->slice.i_nal_ref_idc, p_sys->slice.i_frame_num, p_sys->slice.i_field_pic_flag, p_pic->i_pts - p_pic->i_dts, p_pic->i_pts % (100*CLOCK_FREQ), p_pic->i_length); #endif /* save for next pic fixups */ if( date_Get( &p_sys->dts ) != VLC_TS_INVALID ) { if( p_sys->i_next_block_flags & BLOCK_FLAG_DISCONTINUITY ) date_Set( &p_sys->dts, VLC_TS_INVALID ); else date_Increment( &p_sys->dts, i_num_clock_ts ); } if( p_pic ) { p_pic->i_flags |= p_sys->i_next_block_flags; p_sys->i_next_block_flags = 0; } switch( p_sys->slice.type ) { case H264_SLICE_TYPE_P: p_pic->i_flags |= BLOCK_FLAG_TYPE_P; break; case H264_SLICE_TYPE_B: p_pic->i_flags |= BLOCK_FLAG_TYPE_B; break; case H264_SLICE_TYPE_I: p_pic->i_flags |= BLOCK_FLAG_TYPE_I; default: break; } if( !p_sys->b_recovered ) { if( p_sys->i_recoveryfnum != UINT_MAX ) /* recovering from SEI */ p_pic->i_flags |= BLOCK_FLAG_PREROLL; else p_pic->i_flags |= BLOCK_FLAG_DROP; } p_pic->i_flags &= ~BLOCK_FLAG_PRIVATE_AUD; /* reset after output */ p_sys->i_frame_dts = VLC_TS_INVALID; p_sys->i_frame_pts = VLC_TS_INVALID; p_sys->i_dpb_output_delay = 0; p_sys->i_pic_struct = UINT8_MAX; p_sys->i_recovery_frame_cnt = UINT_MAX; p_sys->slice.type = H264_SLICE_TYPE_UNKNOWN; p_sys->p_sei = NULL; p_sys->pp_sei_last = &p_sys->p_sei; p_sys->b_new_sps = false; p_sys->b_new_pps = false; p_sys->b_slice = false; /* CC */ cc_storage_commit( p_sys->p_ccs, p_pic ); return p_pic; }