/**************************************************************************** * Packetize: the whole thing * Search for the startcodes 3 or more bytes * Feed ParseNALBlock ALWAYS with 4 byte startcode prepended NALs ****************************************************************************/ static block_t *Packetize( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_pic; if( !pp_block || !*pp_block ) return NULL; block_BytestreamPush( &p_sys->bytestream, *pp_block ); for( ;; ) { switch( p_sys->i_state ) { case STATE_NOSYNC: /* Skip until 3 byte startcode 0 0 1 */ if( block_FindStartcodeFromOffset( &p_sys->bytestream, &p_sys->i_offset, p_sys->startcode+1, 3 ) == VLC_SUCCESS) { p_sys->i_state = STATE_NEXT_SYNC; } if( p_sys->i_offset ) { /* skip the data */ block_SkipBytes( &p_sys->bytestream, p_sys->i_offset ); p_sys->i_offset = 0; block_BytestreamFlush( &p_sys->bytestream ); } if( p_sys->i_state != STATE_NEXT_SYNC ) { /* Need more data */ return NULL; } p_sys->i_offset = 1; /* To find next startcode */ case STATE_NEXT_SYNC: /* Find the next 3 byte startcode 0 0 1*/ if( block_FindStartcodeFromOffset( &p_sys->bytestream, &p_sys->i_offset, p_sys->startcode+1, 3 ) != VLC_SUCCESS) { /* Need more data */ return NULL; } /* Get the new fragment and set the pts/dts */ p_pic = block_New( p_dec, p_sys->i_offset +1 ); p_pic->i_pts = p_sys->bytestream.p_block->i_pts; p_pic->i_dts = p_sys->bytestream.p_block->i_dts; /* Force 4 byte startcode 0 0 0 1 */ p_pic->p_buffer[0] = 0; block_GetBytes( &p_sys->bytestream, &p_pic->p_buffer[1], p_pic->i_buffer-1 ); /* Remove trailing 0 bytes */ while( p_pic->i_buffer && (!p_pic->p_buffer[p_pic->i_buffer-1] ) ) p_pic->i_buffer--; p_sys->i_offset = 0; /* Parse the NAL */ if( !( p_pic = ParseNALBlock( p_dec, p_pic ) ) ) { p_sys->i_state = STATE_NOSYNC; break; } #if 0 msg_Dbg( p_dec, "pts="I64Fd" dts="I64Fd, p_pic->i_pts, p_pic->i_dts ); #endif /* So p_block doesn't get re-added several times */ *pp_block = block_BytestreamPop( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; return p_pic; } } }
/***************************************************************************** * Packetize: *****************************************************************************/ static block_t *Packetize( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_pic; if( pp_block == NULL || *pp_block == NULL ) { return NULL; } if( (*pp_block)->i_flags & BLOCK_FLAG_DISCONTINUITY ) { p_sys->i_state = STATE_NOSYNC; 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 = VLC_FALSE; block_Release( *pp_block ); return NULL; } block_BytestreamPush( &p_sys->bytestream, *pp_block ); while( 1 ) { switch( p_sys->i_state ) { case STATE_NOSYNC: if( block_FindStartcodeFromOffset( &p_sys->bytestream, &p_sys->i_offset, p_sys->p_startcode, 3 ) == VLC_SUCCESS ) { p_sys->i_state = STATE_NEXT_SYNC; } if( p_sys->i_offset ) { block_SkipBytes( &p_sys->bytestream, p_sys->i_offset ); p_sys->i_offset = 0; block_BytestreamFlush( &p_sys->bytestream ); } if( p_sys->i_state != STATE_NEXT_SYNC ) { /* Need more data */ return NULL; } p_sys->i_offset = 1; /* To find next startcode */ case STATE_NEXT_SYNC: /* TODO: If p_block == NULL, flush the buffer without checking the * next sync word */ /* Find the next startcode */ if( block_FindStartcodeFromOffset( &p_sys->bytestream, &p_sys->i_offset, p_sys->p_startcode, 3 ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } /* Get the new fragment and set the pts/dts */ p_pic = block_New( p_dec, p_sys->i_offset ); block_BytestreamFlush( &p_sys->bytestream ); p_pic->i_pts = p_sys->bytestream.p_block->i_pts; p_pic->i_dts = p_sys->bytestream.p_block->i_dts; block_GetBytes( &p_sys->bytestream, p_pic->p_buffer, p_pic->i_buffer ); /* don't reuse the same timestamps several times */ if( p_pic->i_buffer >= 4 && p_pic->p_buffer[3] == 0x00 ) { /* We have a picture start code */ p_sys->bytestream.p_block->i_pts = 0; p_sys->bytestream.p_block->i_dts = 0; } p_sys->i_offset = 0; /* Get picture if any */ if( !( p_pic = ParseMPEGBlock( p_dec, p_pic ) ) ) { p_sys->i_state = STATE_NOSYNC; break; } /* We've just started the stream, wait for the first PTS. * We discard here so we can still get the sequence header. */ if( p_sys->i_dts <= 0 && p_sys->i_pts <= 0 && p_sys->i_interpolated_dts <= 0 ) { msg_Dbg( p_dec, "need a starting pts/dts" ); p_sys->i_state = STATE_NOSYNC; block_Release( p_pic ); break; } /* When starting the stream we can have the first frame with * a null DTS (i_interpolated_pts is initialized to 0) */ if( !p_pic->i_dts ) p_pic->i_dts = p_pic->i_pts; /* So p_block doesn't get re-added several times */ *pp_block = block_BytestreamPop( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; return p_pic; } } }
/*** * Bytestream synchronizer * maps [Bytes] -> DataUnit */ static block_t *dirac_DoSync( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; parse_info_t pu; static const uint8_t p_parsecode[4] = {'B','B','C','D'}; do { switch( p_sys->i_state ) { case NOT_SYNCED: { if( VLC_SUCCESS != block_FindStartcodeFromOffset( &p_sys->bytestream, &p_sys->i_offset, p_parsecode, 4 ) ) { /* p_sys->i_offset will have been set to: * end of bytestream - amount of prefix found * can resume search from this point when more data arrives */ return NULL; } /* candidate parse_code_prefix has been found at p_sys->i_offset */ if( VLC_SUCCESS != block_PeekOffsetBytes( &p_sys->bytestream, p_sys->i_offset + 12, NULL, 0 ) ) { /* insufficient data has been accumulated to fully extract * a parse_info header. exit for now in the hope of more * data later to retry at exactly the same point */ return NULL; } p_sys->i_state = TRY_SYNC; break; /* candidate found, try to sync */ } case SYNCED: /* -> TRY_SYNC | NOT_SYNCED */ /* sanity: can only reach this after having extracted a DU, * which causes data to be consumed and local i_offset to be reset */ assert( p_sys->i_offset == 0 ); if( VLC_SUCCESS != block_PeekOffsetBytes( &p_sys->bytestream, 12, NULL, 0 ) ) { /* insufficient data has been accumulated to fully extract * a parse_info header, retry later */ return NULL; } if( !dirac_UnpackParseInfo( &pu, &p_sys->bytestream, 0 ) || !pu.u_next_offset || (p_sys->u_last_npo != pu.u_prev_offset) ) { /* !a: not a valid parse info. * !pu.u_next_offset: don't know the length of the data unit * search for the next one that points back * to this one to determine length. * (p_sys->u_last_npo != pu.u_prev_offset): some desync */ p_sys->i_state = NOT_SYNCED; break; } if( pu.u_next_offset > 1024*1024 ) { /* sanity check for erronious hugs next_parse_offsets * (eg, 2^32-1) that would cause a very long wait * and large space consumption: fall back to try sync */ p_sys->i_state = TRY_SYNC; break; } /* check that the start of the next data unit is avaliable */ if( VLC_SUCCESS != block_PeekOffsetBytes( &p_sys->bytestream, pu.u_next_offset + 12, NULL, 0 ) ) { return NULL; /* retry later */ } /* attempt to synchronise backwards from pu.u_next_offset */ p_sys->i_offset = pu.u_next_offset; /* fall through */ case TRY_SYNC: /* -> SYNCED | NOT_SYNCED */ { if( !p_sys->i_offset ) goto sync_fail; /* if a is at start of bytestream, b can't be in buffer */ parse_info_t pu_a; bool a = dirac_UnpackParseInfo( &pu_a, &p_sys->bytestream, p_sys->i_offset ); if( !a || (pu_a.u_prev_offset > p_sys->i_offset) ) goto sync_fail; /* b lies beyond start of bytestream: can't sync */ if( !pu_a.u_prev_offset ) { if( p_sys->i_state == TRY_SYNC ) { goto sync_fail; /* can't find different pu_b from pu_a */ } /* state == SYNCED: already know where pu_b is. * pu_a has probably been inserted by something that doesn't * know what the last next_parse_offset was */ pu_a.u_prev_offset = pu.u_next_offset; } parse_info_t *pu_b = &pu; bool b = dirac_UnpackParseInfo( pu_b, &p_sys->bytestream, p_sys->i_offset - pu_a.u_prev_offset ); if( !b || (pu_b->u_next_offset && pu_a.u_prev_offset != pu_b->u_next_offset) ) { /* if pu_b->u_next_offset = 0, have to assume we've synced, ie, * just rely on finding a valid pu_b from pu_a. */ goto sync_fail; } p_sys->u_last_npo = pu_b->u_next_offset; if( !pu_b->u_next_offset ) pu_b->u_next_offset = pu_a.u_prev_offset; /* offset was pointing at pu_a, rewind to point at pu_b */ p_sys->i_offset -= pu_a.u_prev_offset; p_sys->i_state = SYNCED; break; } sync_fail: if( p_sys->i_state == SYNCED ) p_sys->i_offset = 0; p_sys->i_offset++; p_sys->i_state = NOT_SYNCED; break; /* find somewhere else to try again */ default:; } } while( SYNCED != p_sys->i_state ); /* * synced, attempt to extract a data unit */ /* recover any timestamps from the data that is about to be flushed */ dirac_RecoverTimestamps( p_dec, p_sys->i_offset ); /* flush everything up to the start of the DU */ block_SkipBytes( &p_sys->bytestream, p_sys->i_offset ); block_BytestreamFlush( &p_sys->bytestream ); p_sys->i_offset = 0; /* setup the data unit buffer */ block_t *p_block = block_Alloc( pu.u_next_offset ); if( !p_block ) return NULL; p_block->i_pts = p_sys->i_sync_pts; p_block->i_dts = p_sys->i_sync_dts; p_sys->i_sync_pts = p_sys->i_sync_dts = VLC_TS_INVALID; /* recover any new timestamps from the data that is about to be consumed */ dirac_RecoverTimestamps( p_dec, p_sys->i_offset ); block_GetBytes( &p_sys->bytestream, p_block->p_buffer, p_block->i_buffer ); /* save parse offset in private area for later use */ dirac_block_encap_t *p_dbe = dirac_NewBlockEncap( &p_block ); if( p_dbe ) p_dbe->u_last_next_offset = pu.u_next_offset; return p_block; }
/**************************************************************************** * Packetize: the whole thing ****************************************************************************/ static block_t *Packetize( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_pic; if( !pp_block || !*pp_block ) return NULL; block_BytestreamPush( &p_sys->bytestream, *pp_block ); for( ;; ) { switch( p_sys->i_state ) { case STATE_NOSYNC: if( block_FindStartcodeFromOffset( &p_sys->bytestream, &p_sys->i_offset, p_sys->startcode+1, 3 ) == VLC_SUCCESS) { p_sys->i_state = STATE_NEXT_SYNC; } if( p_sys->i_offset ) { block_SkipBytes( &p_sys->bytestream, p_sys->i_offset ); p_sys->i_offset = 0; block_BytestreamFlush( &p_sys->bytestream ); } if( p_sys->i_state != STATE_NEXT_SYNC ) { /* Need more data */ return NULL; } p_sys->i_offset = 1; /* To find next startcode */ case STATE_NEXT_SYNC: /* Find the next startcode */ if( block_FindStartcodeFromOffset( &p_sys->bytestream, &p_sys->i_offset, p_sys->startcode, 3 ) != VLC_SUCCESS) { if( block_FindStartcodeFromOffset( &p_sys->bytestream, &p_sys->i_offset, p_sys->startcode+1, 3 ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } } /* Get the new fragment and set the pts/dts */ p_pic = block_New( p_dec, p_sys->i_offset ); p_pic->i_pts = p_sys->bytestream.p_block->i_pts; p_pic->i_dts = p_sys->bytestream.p_block->i_dts; block_GetBytes( &p_sys->bytestream, p_pic->p_buffer, p_pic->i_buffer ); p_sys->i_offset = 0; /* Parse the NAL */ if( !( p_pic = ParseNALBlock( p_dec, p_pic ) ) ) { p_sys->i_state = STATE_NOSYNC; break; } /* So p_block doesn't get re-added several times */ *pp_block = block_BytestreamPop( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; return p_pic; } } }