/***************************************************************************** * Reset: reset the decoder state *****************************************************************************/ static void Reset( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; cc_Flush( &p_sys->cc ); mpeg2_reset( p_sys->p_mpeg2dec, 0 ); DpbClean( p_dec ); }
/***************************************************************************** * GetCc: *****************************************************************************/ static block_t *GetCc( decoder_t *p_dec, bool pb_present[4] ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_cc; for( int i = 0; i < 4; i++ ) pb_present[i] = p_sys->cc.pb_present[i]; if( p_sys->cc.i_data <= 0 ) return NULL; p_cc = block_Alloc( p_sys->cc.i_data); if( p_cc ) { memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data ); p_cc->i_dts = p_cc->i_pts = p_sys->cc.b_reorder ? p_sys->i_cc_pts : p_sys->i_cc_dts; p_cc->i_flags = ( p_sys->cc.b_reorder ? p_sys->i_cc_flags : BLOCK_FLAG_TYPE_P ) & BLOCK_FLAG_TYPE_MASK; } cc_Flush( &p_sys->cc ); return p_cc; }
/***************************************************************************** * RunDecoder: the libmpeg2 decoder *****************************************************************************/ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; mpeg2_state_t state; picture_t *p_pic; block_t *p_block; if( !pp_block || !*pp_block ) return NULL; p_block = *pp_block; if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED) ) { cc_Flush( &p_sys->cc ); mpeg2_reset( p_sys->p_mpeg2dec, p_sys->p_info->sequence != NULL ); DpbClean( p_dec ); } while( 1 ) { state = mpeg2_parse( p_sys->p_mpeg2dec ); switch( state ) { case STATE_SEQUENCE: { /* */ DpbClean( p_dec ); /* */ mpeg2_custom_fbuf( p_sys->p_mpeg2dec, 1 ); /* Set the first 2 reference frames */ GetAR( p_dec ); for( int i = 0; i < 2; i++ ) { picture_t *p_picture = DpbNewPicture( p_dec ); if( !p_picture ) { /* Is it ok ? or do we need a reset ? */ block_Release( p_block ); return NULL; } PutPicture( p_dec, p_picture ); } if( p_sys->p_synchro ) decoder_SynchroRelease( p_sys->p_synchro ); p_sys->p_synchro = decoder_SynchroInit( p_dec, (uint32_t)((uint64_t)1001000000 * 27 / p_sys->p_info->sequence->frame_period) ); p_sys->b_after_sequence_header = true; break; } case STATE_GOP: /* There can be userdata in a GOP. It needs to be remembered for the next picture. */ if( p_sys->p_info->user_data_len > 2 ) { free( p_sys->p_gop_user_data ); p_sys->p_gop_user_data = calloc( p_sys->p_info->user_data_len, sizeof(uint8_t) ); if( p_sys->p_gop_user_data ) { p_sys->i_gop_user_data = p_sys->p_info->user_data_len; memcpy( p_sys->p_gop_user_data, p_sys->p_info->user_data, p_sys->p_info->user_data_len ); } } break; case STATE_PICTURE: { const mpeg2_info_t *p_info = p_sys->p_info; const mpeg2_picture_t *p_current = p_info->current_picture; mtime_t i_pts, i_dts; if( p_sys->b_after_sequence_header && (p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_P ) { /* Intra-slice refresh. Simulate a blank I picture. */ msg_Dbg( p_dec, "intra-slice refresh stream" ); decoder_SynchroNewPicture( p_sys->p_synchro, I_CODING_TYPE, 2, 0, 0, p_info->sequence->flags & SEQ_FLAG_LOW_DELAY ); decoder_SynchroDecode( p_sys->p_synchro ); decoder_SynchroEnd( p_sys->p_synchro, I_CODING_TYPE, 0 ); p_sys->b_slice_i = true; } p_sys->b_after_sequence_header = false; #ifdef PIC_FLAG_PTS i_pts = p_current->flags & PIC_FLAG_PTS ? ( ( p_current->pts == (uint32_t)p_sys->i_current_pts ) ? p_sys->i_current_pts : p_sys->i_previous_pts ) : 0; i_dts = 0; /* Hack to handle demuxers which only have DTS timestamps */ if( !i_pts && !p_block->i_pts && p_block->i_dts > 0 ) { if( p_info->sequence->flags & SEQ_FLAG_LOW_DELAY || (p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_B ) { i_pts = p_block->i_dts; } } p_block->i_pts = p_block->i_dts = 0; /* End hack */ #else /* New interface */ i_pts = p_current->flags & PIC_FLAG_TAGS ? ( ( p_current->tag == (uint32_t)p_sys->i_current_pts ) ? p_sys->i_current_pts : p_sys->i_previous_pts ) : 0; i_dts = p_current->flags & PIC_FLAG_TAGS ? ( ( p_current->tag2 == (uint32_t)p_sys->i_current_dts ) ? p_sys->i_current_dts : p_sys->i_previous_dts ) : 0; #endif /* If nb_fields == 1, it is a field picture, and it will be * followed by another field picture for which we won't call * decoder_SynchroNewPicture() because this would have other * problems, so we take it into account here. * This kind of sucks, but I didn't think better. --Meuuh */ decoder_SynchroNewPicture( p_sys->p_synchro, p_current->flags & PIC_MASK_CODING_TYPE, p_current->nb_fields == 1 ? 2 : p_current->nb_fields, i_pts, i_dts, p_info->sequence->flags & SEQ_FLAG_LOW_DELAY ); bool b_skip = false; if( !p_dec->b_pace_control && !p_sys->b_preroll && !(p_sys->b_slice_i && ((p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_P)) && !decoder_SynchroChoose( p_sys->p_synchro, p_current->flags & PIC_MASK_CODING_TYPE, /*p_sys->p_vout->render_time*/ 0 /*FIXME*/, p_info->sequence->flags & SEQ_FLAG_LOW_DELAY ) ) { b_skip = true; } p_pic = NULL; if( !b_skip ) p_pic = DpbNewPicture( p_dec ); if( b_skip || !p_pic ) { mpeg2_skip( p_sys->p_mpeg2dec, 1 ); p_sys->b_skip = true; decoder_SynchroTrash( p_sys->p_synchro ); PutPicture( p_dec, NULL ); if( !b_skip ) { block_Release( p_block ); return NULL; } } else { mpeg2_skip( p_sys->p_mpeg2dec, 0 ); p_sys->b_skip = false; decoder_SynchroDecode( p_sys->p_synchro ); PutPicture( p_dec, p_pic ); } if( p_info->user_data_len > 2 || p_sys->i_gop_user_data > 2 ) { p_sys->i_cc_pts = i_pts; p_sys->i_cc_dts = i_dts; if( (p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_P ) p_sys->i_cc_flags = BLOCK_FLAG_TYPE_P; else if( (p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_B ) p_sys->i_cc_flags = BLOCK_FLAG_TYPE_B; else p_sys->i_cc_flags = BLOCK_FLAG_TYPE_I; if( p_sys->i_gop_user_data > 2 ) { /* We now have picture info for any cached user_data out of the gop */ cc_Extract( &p_sys->cc, &p_sys->p_gop_user_data[0], p_sys->i_gop_user_data ); p_sys->i_gop_user_data = 0; } /* Extract the CC from the user_data of the picture */ if( p_info->user_data_len > 2 ) cc_Extract( &p_sys->cc, &p_info->user_data[0], p_info->user_data_len ); } } break; case STATE_BUFFER: if( !p_block->i_buffer ) { block_Release( p_block ); return NULL; } if( (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED)) && p_sys->p_synchro && p_sys->p_info->sequence && p_sys->p_info->sequence->width != (unsigned)-1 ) { decoder_SynchroReset( p_sys->p_synchro ); if( p_sys->p_info->current_fbuf != NULL && p_sys->p_info->current_fbuf->id != NULL ) { p_sys->b_garbage_pic = true; } if( p_sys->b_slice_i ) { decoder_SynchroNewPicture( p_sys->p_synchro, I_CODING_TYPE, 2, 0, 0, p_sys->p_info->sequence->flags & SEQ_FLAG_LOW_DELAY ); decoder_SynchroDecode( p_sys->p_synchro ); decoder_SynchroEnd( p_sys->p_synchro, I_CODING_TYPE, 0 ); } } if( p_block->i_flags & BLOCK_FLAG_PREROLL ) { p_sys->b_preroll = true; } else if( p_sys->b_preroll ) { p_sys->b_preroll = false; if( p_sys->p_synchro ) decoder_SynchroReset( p_sys->p_synchro ); } #ifdef PIC_FLAG_PTS if( p_block->i_pts ) { mpeg2_pts( p_sys->p_mpeg2dec, (uint32_t)p_block->i_pts ); #else /* New interface */ if( p_block->i_pts || p_block->i_dts ) { mpeg2_tag_picture( p_sys->p_mpeg2dec, (uint32_t)p_block->i_pts, (uint32_t)p_block->i_dts ); #endif p_sys->i_previous_pts = p_sys->i_current_pts; p_sys->i_current_pts = p_block->i_pts; p_sys->i_previous_dts = p_sys->i_current_dts; p_sys->i_current_dts = p_block->i_dts; } mpeg2_buffer( p_sys->p_mpeg2dec, p_block->p_buffer, p_block->p_buffer + p_block->i_buffer ); p_block->i_buffer = 0; break; #if MPEG2_RELEASE >= MPEG2_VERSION (0, 5, 0) case STATE_SEQUENCE_MODIFIED: GetAR( p_dec ); break; #endif case STATE_PICTURE_2ND: p_sys->b_second_field = true; break; case STATE_INVALID_END: case STATE_END: case STATE_SLICE: p_pic = NULL; if( p_sys->p_info->display_fbuf && p_sys->p_info->display_fbuf->id ) { p_pic = p_sys->p_info->display_fbuf->id; DpbDisplayPicture( p_dec, p_pic ); decoder_SynchroEnd( p_sys->p_synchro, p_sys->p_info->display_picture->flags & PIC_MASK_CODING_TYPE, p_sys->b_garbage_pic ); p_pic->date = decoder_SynchroDate( p_sys->p_synchro ); if( p_sys->b_garbage_pic ) p_pic->date = 0; /* ??? */ p_sys->b_garbage_pic = false; } if( p_sys->p_info->discard_fbuf && p_sys->p_info->discard_fbuf->id ) { DpbUnlinkPicture( p_dec, p_sys->p_info->discard_fbuf->id ); } /* For still frames */ if( state == STATE_END && p_pic ) p_pic->b_force = true; if( p_pic ) { /* Avoid frames with identical timestamps. * Especially needed for still frames in DVD menus. */ if( p_sys->i_last_frame_pts == p_pic->date ) p_pic->date++; p_sys->i_last_frame_pts = p_pic->date; return p_pic; } break; case STATE_INVALID: { msg_Err( p_dec, "invalid picture encountered" ); /* I don't think we have anything to do, but well without * docs ... */ break; } default: break; } } /* Never reached */ return NULL; } /***************************************************************************** * CloseDecoder: libmpeg2 decoder destruction *****************************************************************************/ static void CloseDecoder( vlc_object_t *p_this ) { decoder_t *p_dec = (decoder_t *)p_this; decoder_sys_t *p_sys = p_dec->p_sys; DpbClean( p_dec ); free( p_sys->p_gop_user_data ); if( p_sys->p_synchro ) decoder_SynchroRelease( p_sys->p_synchro ); if( p_sys->p_mpeg2dec ) mpeg2_close( p_sys->p_mpeg2dec ); free( p_sys ); }
/***************************************************************************** * ParseMPEGBlock: Re-assemble fragments into a block containing a picture *****************************************************************************/ static block_t *ParseMPEGBlock( decoder_t *p_dec, block_t *p_frag ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_pic = NULL; /* * Check if previous picture is finished */ if( ( p_sys->b_frame_slice && (p_frag->p_buffer[3] == 0x00 || p_frag->p_buffer[3] > 0xaf) ) && p_sys->p_seq == NULL ) { /* We have a picture but without a sequence header we can't * do anything */ msg_Dbg( p_dec, "waiting for sequence start" ); if( p_sys->p_frame ) block_ChainRelease( p_sys->p_frame ); p_sys->p_frame = NULL; p_sys->pp_last = &p_sys->p_frame; p_sys->b_frame_slice = false; } else if( p_sys->b_frame_slice && (p_frag->p_buffer[3] == 0x00 || p_frag->p_buffer[3] > 0xaf) ) { const bool b_eos = p_frag->p_buffer[3] == 0xb7; mtime_t i_duration; if( b_eos ) { block_ChainLastAppend( &p_sys->pp_last, p_frag ); p_frag = NULL; } p_pic = block_ChainGather( p_sys->p_frame ); if( b_eos ) p_pic->i_flags |= BLOCK_FLAG_END_OF_SEQUENCE; i_duration = (mtime_t)( 1000000 * p_sys->i_frame_rate_base / p_sys->i_frame_rate ); if( !p_sys->b_seq_progressive && p_sys->i_picture_structure != 0x03 ) { i_duration /= 2; } if( p_sys->b_seq_progressive ) { if( p_sys->i_top_field_first == 0 && p_sys->i_repeat_first_field == 1 ) { i_duration *= 2; } else if( p_sys->i_top_field_first == 1 && p_sys->i_repeat_first_field == 1 ) { i_duration *= 3; } } else { if( p_sys->i_picture_structure == 0x03 ) { if( p_sys->i_progressive_frame && p_sys->i_repeat_first_field ) { i_duration += i_duration / 2; } } } if( p_sys->b_low_delay || p_sys->i_picture_type == 0x03 ) { /* Trivial case (DTS == PTS) */ /* Correct interpolated dts when we receive a new pts/dts */ if( p_sys->i_pts > VLC_TS_INVALID ) p_sys->i_interpolated_dts = p_sys->i_pts; if( p_sys->i_dts > VLC_TS_INVALID ) p_sys->i_interpolated_dts = p_sys->i_dts; } else { /* Correct interpolated dts when we receive a new pts/dts */ if(p_sys->i_last_ref_pts > VLC_TS_INVALID && !p_sys->b_second_field) p_sys->i_interpolated_dts = p_sys->i_last_ref_pts; if( p_sys->i_dts > VLC_TS_INVALID ) p_sys->i_interpolated_dts = p_sys->i_dts; if( !p_sys->b_second_field ) p_sys->i_last_ref_pts = p_sys->i_pts; } p_pic->i_dts = p_sys->i_interpolated_dts; p_sys->i_interpolated_dts += i_duration; /* Set PTS only if we have a B frame or if it comes from the stream */ if( p_sys->i_pts > VLC_TS_INVALID ) { p_pic->i_pts = p_sys->i_pts; } else if( p_sys->i_picture_type == 0x03 ) { p_pic->i_pts = p_pic->i_dts; } else { p_pic->i_pts = VLC_TS_INVALID; } switch ( p_sys->i_picture_type ) { case 0x01: p_pic->i_flags |= BLOCK_FLAG_TYPE_I; break; case 0x02: p_pic->i_flags |= BLOCK_FLAG_TYPE_P; break; case 0x03: p_pic->i_flags |= BLOCK_FLAG_TYPE_B; break; } p_pic->i_length = p_sys->i_interpolated_dts - p_pic->i_dts; #if 0 msg_Dbg( p_dec, "pic: type=%d dts=%"PRId64" pts-dts=%"PRId64, p_sys->i_picture_type, p_pic->i_dts, p_pic->i_pts - p_pic->i_dts); #endif /* Reset context */ p_sys->p_frame = NULL; p_sys->pp_last = &p_sys->p_frame; p_sys->b_frame_slice = false; if( p_sys->i_picture_structure != 0x03 ) { p_sys->b_second_field = !p_sys->b_second_field; } else { p_sys->b_second_field = 0; } /* CC */ p_sys->b_cc_reset = true; p_sys->i_cc_pts = p_pic->i_pts; p_sys->i_cc_dts = p_pic->i_dts; p_sys->i_cc_flags = p_pic->i_flags; } if( !p_pic && p_sys->b_cc_reset ) { p_sys->b_cc_reset = false; cc_Flush( &p_sys->cc ); } if( !p_frag ) return p_pic; /* * Check info of current fragment */ if( p_frag->p_buffer[3] == 0xb8 ) { /* Group start code */ if( p_sys->p_seq && p_sys->i_seq_old > p_sys->i_frame_rate/p_sys->i_frame_rate_base ) { /* Useful for mpeg1: repeat sequence header every second */ block_ChainLastAppend( &p_sys->pp_last, block_Duplicate( p_sys->p_seq ) ); if( p_sys->p_ext ) { block_ChainLastAppend( &p_sys->pp_last, block_Duplicate( p_sys->p_ext ) ); } p_sys->i_seq_old = 0; } } else if( p_frag->p_buffer[3] == 0xb3 && p_frag->i_buffer >= 8 ) { /* Sequence header code */ static const int code_to_frame_rate[16][2] = { { 1, 1 }, /* invalid */ { 24000, 1001 }, { 24, 1 }, { 25, 1 }, { 30000, 1001 }, { 30, 1 }, { 50, 1 }, { 60000, 1001 }, { 60, 1 }, /* Unofficial 15fps from Xing*/ { 15, 1001 }, /* Unofficial economy rates from libmpeg3 */ { 5000, 1001 }, { 1000, 1001 }, { 12000, 1001 }, { 15000, 1001 }, { 1, 1 }, { 1, 1 } /* invalid */ }; if( p_sys->p_seq ) block_Release( p_sys->p_seq ); if( p_sys->p_ext ) block_Release( p_sys->p_ext ); p_sys->p_seq = block_Duplicate( p_frag ); p_sys->i_seq_old = 0; p_sys->p_ext = NULL; p_dec->fmt_out.video.i_width = ( p_frag->p_buffer[4] << 4)|(p_frag->p_buffer[5] >> 4 ); p_dec->fmt_out.video.i_height = ( (p_frag->p_buffer[5]&0x0f) << 8 )|p_frag->p_buffer[6]; p_sys->i_aspect_ratio_info = p_frag->p_buffer[7] >> 4; /* TODO: MPEG1 aspect ratio */ p_sys->i_frame_rate = code_to_frame_rate[p_frag->p_buffer[7]&0x0f][0]; p_sys->i_frame_rate_base = code_to_frame_rate[p_frag->p_buffer[7]&0x0f][1]; p_dec->fmt_out.video.i_frame_rate = p_sys->i_frame_rate; p_dec->fmt_out.video.i_frame_rate_base = p_sys->i_frame_rate_base; p_sys->b_seq_progressive = true; p_sys->b_low_delay = true; if ( !p_sys->b_inited ) { msg_Dbg( p_dec, "size %dx%d fps=%.3f", p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height, p_sys->i_frame_rate / (float)p_sys->i_frame_rate_base ); p_sys->b_inited = 1; } } else if( p_frag->p_buffer[3] == 0xb5 )
static block_t *OutputPicture( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_pic; if ( !p_sys->b_header && p_sys->i_recovery_frames != -1 ) { if( p_sys->i_recovery_frames == 0 ) { msg_Dbg( p_dec, "Recovery from SEI recovery point complete" ); p_sys->b_header = true; } --p_sys->i_recovery_frames; } if( !p_sys->b_header && p_sys->i_recovery_frames == -1 && p_sys->slice.i_frame_type != BLOCK_FLAG_TYPE_I) return NULL; const bool b_sps_pps_i = p_sys->slice.i_frame_type == BLOCK_FLAG_TYPE_I && p_sys->b_sps && p_sys->b_pps; if( b_sps_pps_i || p_sys->b_frame_sps || p_sys->b_frame_pps ) { block_t *p_head = NULL; if( p_sys->p_frame->i_flags & BLOCK_FLAG_PRIVATE_AUD ) { p_head = p_sys->p_frame; p_sys->p_frame = p_sys->p_frame->p_next; } block_t *p_list = NULL; for( int i = 0; i < SPS_MAX && (b_sps_pps_i || p_sys->b_frame_sps); i++ ) { if( p_sys->pp_sps[i] ) block_ChainAppend( &p_list, block_Duplicate( p_sys->pp_sps[i] ) ); } for( int i = 0; i < PPS_MAX && (b_sps_pps_i || p_sys->b_frame_pps); i++ ) { if( p_sys->pp_pps[i] ) block_ChainAppend( &p_list, block_Duplicate( p_sys->pp_pps[i] ) ); } if( b_sps_pps_i && p_list ) p_sys->b_header = true; if( p_head ) p_head->p_next = p_list; else p_head = p_list; block_ChainAppend( &p_head, p_sys->p_frame ); p_pic = block_ChainGather( p_head ); } else { p_pic = block_ChainGather( p_sys->p_frame ); } p_pic->i_dts = p_sys->i_frame_dts; p_pic->i_pts = p_sys->i_frame_pts; p_pic->i_length = 0; /* FIXME */ p_pic->i_flags |= p_sys->slice.i_frame_type; p_pic->i_flags &= ~BLOCK_FLAG_PRIVATE_AUD; if( !p_sys->b_header ) p_pic->i_flags |= BLOCK_FLAG_PREROLL; p_sys->slice.i_frame_type = 0; p_sys->p_frame = NULL; p_sys->i_frame_dts = VLC_TS_INVALID; p_sys->i_frame_pts = VLC_TS_INVALID; p_sys->b_frame_sps = false; p_sys->b_frame_pps = false; p_sys->b_slice = false; /* CC */ p_sys->i_cc_pts = p_pic->i_pts; p_sys->i_cc_dts = p_pic->i_dts; p_sys->i_cc_flags = p_pic->i_flags; p_sys->cc = p_sys->cc_next; cc_Flush( &p_sys->cc_next ); return p_pic; }
/***************************************************************************** * ParseNALBlock: parses annexB type NALs * All p_frag blocks are required to start with 0 0 0 1 4-byte startcode *****************************************************************************/ static block_t *ParseNALBlock( decoder_t *p_dec, bool *pb_ts_used, block_t *p_frag ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_pic = NULL; const int i_nal_ref_idc = (p_frag->p_buffer[4] >> 5)&0x03; const int i_nal_type = p_frag->p_buffer[4]&0x1f; const mtime_t i_frag_dts = p_frag->i_dts; const mtime_t i_frag_pts = p_frag->i_pts; if( p_sys->b_slice && ( !p_sys->b_sps || !p_sys->b_pps ) ) { block_ChainRelease( p_sys->p_frame ); msg_Warn( p_dec, "waiting for SPS/PPS" ); /* Reset context */ p_sys->slice.i_frame_type = 0; p_sys->p_frame = NULL; p_sys->b_frame_sps = false; p_sys->b_frame_pps = false; p_sys->b_slice = false; cc_Flush( &p_sys->cc_next ); } if( ( !p_sys->b_sps || !p_sys->b_pps ) && i_nal_type >= NAL_SLICE && i_nal_type <= NAL_SLICE_IDR ) { p_sys->b_slice = true; /* Fragment will be discarded later on */ } else if( i_nal_type >= NAL_SLICE && i_nal_type <= NAL_SLICE_IDR ) { slice_t slice; bool b_new_picture; ParseSlice( p_dec, &b_new_picture, &slice, i_nal_ref_idc, i_nal_type, p_frag ); /* */ if( b_new_picture && p_sys->b_slice ) p_pic = OutputPicture( p_dec ); /* */ p_sys->slice = slice; p_sys->b_slice = true; } else if( i_nal_type == NAL_SPS ) { if( p_sys->b_slice ) p_pic = OutputPicture( p_dec ); p_sys->b_frame_sps = true; PutSPS( p_dec, p_frag ); /* Do not append the SPS because we will insert it on keyframes */ p_frag = NULL; } else if( i_nal_type == NAL_PPS ) { if( p_sys->b_slice ) p_pic = OutputPicture( p_dec ); p_sys->b_frame_pps = true; PutPPS( p_dec, p_frag ); /* Do not append the PPS because we will insert it on keyframes */ p_frag = NULL; } else if( i_nal_type == NAL_AU_DELIMITER || i_nal_type == NAL_SEI || ( i_nal_type >= 13 && i_nal_type <= 18 ) ) { if( p_sys->b_slice ) p_pic = OutputPicture( p_dec ); /* Parse SEI for CC support */ if( i_nal_type == NAL_SEI ) { ParseSei( p_dec, p_frag ); } else if( i_nal_type == NAL_AU_DELIMITER ) { if( p_sys->p_frame && (p_sys->p_frame->i_flags & BLOCK_FLAG_PRIVATE_AUD) ) { block_Release( p_frag ); p_frag = NULL; } else { p_frag->i_flags |= BLOCK_FLAG_PRIVATE_AUD; } } } /* Append the block */ if( p_frag ) block_ChainAppend( &p_sys->p_frame, p_frag ); *pb_ts_used = false; if( p_sys->i_frame_dts <= VLC_TS_INVALID && p_sys->i_frame_pts <= VLC_TS_INVALID ) { p_sys->i_frame_dts = i_frag_dts; p_sys->i_frame_pts = i_frag_pts; *pb_ts_used = true; } return p_pic; }
static block_t *OutputPicture( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_pic; if ( !p_sys->b_header && p_sys->i_recovery_frames != -1 ) { if( p_sys->i_recovery_frames == 0 ) { msg_Dbg( p_dec, "Recovery from SEI recovery point complete" ); p_sys->b_header = true; } --p_sys->i_recovery_frames; } if( !p_sys->b_header && p_sys->i_recovery_frames == -1 && p_sys->slice.i_frame_type != BLOCK_FLAG_TYPE_I) return NULL; const bool b_sps_pps_i = p_sys->slice.i_frame_type == BLOCK_FLAG_TYPE_I && p_sys->b_sps && p_sys->b_pps; if( b_sps_pps_i || p_sys->b_frame_sps || p_sys->b_frame_pps ) { block_t *p_head = NULL; if( p_sys->p_frame->i_flags & BLOCK_FLAG_PRIVATE_AUD ) { p_head = p_sys->p_frame; p_sys->p_frame = p_sys->p_frame->p_next; } block_t *p_list = NULL; for( int i = 0; i < SPS_MAX && (b_sps_pps_i || p_sys->b_frame_sps); i++ ) { if( p_sys->pp_sps[i] ) block_ChainAppend( &p_list, block_Duplicate( p_sys->pp_sps[i] ) ); } for( int i = 0; i < PPS_MAX && (b_sps_pps_i || p_sys->b_frame_pps); i++ ) { if( p_sys->pp_pps[i] ) block_ChainAppend( &p_list, block_Duplicate( p_sys->pp_pps[i] ) ); } if( b_sps_pps_i && p_list ) p_sys->b_header = true; if( p_head ) p_head->p_next = p_list; else p_head = p_list; block_ChainAppend( &p_head, p_sys->p_frame ); p_pic = block_ChainGather( p_head ); } else { p_pic = block_ChainGather( p_sys->p_frame ); } p_pic->i_dts = p_sys->i_frame_dts; p_pic->i_pts = p_sys->i_frame_pts; p_pic->i_length = 0; /* FIXME */ p_pic->i_flags |= p_sys->slice.i_frame_type; p_pic->i_flags &= ~BLOCK_FLAG_PRIVATE_AUD; if( !p_sys->b_header ) p_pic->i_flags |= BLOCK_FLAG_PREROLL; if( p_sys->b_frame_mbs_only == 0 && p_sys->b_pic_struct_present_flag ) { switch( p_sys->i_pic_struct ) { case 1: if( p_sys->i_fields_dts == 2 ) p_pic->i_flags |= BLOCK_FLAG_TOP_FIELD_FIRST; else p_pic->i_flags |= BLOCK_FLAG_BOTTOM_FIELD_FIRST; break; case 3: case 5: p_pic->i_flags |= BLOCK_FLAG_TOP_FIELD_FIRST; break; case 2: if( p_sys->i_fields_dts == 2 ) p_pic->i_flags |= BLOCK_FLAG_BOTTOM_FIELD_FIRST; else p_pic->i_flags |= BLOCK_FLAG_TOP_FIELD_FIRST; break; case 4: case 6: p_pic->i_flags |= BLOCK_FLAG_BOTTOM_FIELD_FIRST; break; default: break; } } p_sys->i_fields_dts -= (1 + p_sys->b_frame_mbs_only); if( p_sys->i_fields_dts <= 0 ) { p_sys->i_frame_dts = VLC_TS_INVALID; p_sys->i_frame_pts = VLC_TS_INVALID; } else if( p_sys->b_timing_info_present_flag && p_sys->i_time_scale ) { p_sys->i_frame_pts += CLOCK_FREQ * p_sys->i_num_units_in_tick / p_sys->i_time_scale; } else p_sys->i_frame_pts = VLC_TS_INVALID; p_sys->slice.i_frame_type = 0; p_sys->p_frame = NULL; p_sys->b_frame_sps = false; p_sys->b_frame_pps = false; p_sys->b_slice = false; /* CC */ p_sys->i_cc_pts = p_pic->i_pts; p_sys->i_cc_dts = p_pic->i_dts; p_sys->i_cc_flags = p_pic->i_flags; p_sys->cc = p_sys->cc_next; cc_Flush( &p_sys->cc_next ); return p_pic; }