/**************************************************************************** * DecodeBlock: the whole thing **************************************************************************** * This function is called just after the thread is launched. ****************************************************************************/ static block_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; uint8_t p_header[VLC_A52_HEADER_SIZE]; uint8_t *p_buf; block_t *p_out_buffer; if( !pp_block || !*pp_block ) return NULL; if( (*pp_block)->i_flags&(BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { if( (*pp_block)->i_flags&BLOCK_FLAG_CORRUPTED ) { p_sys->i_state = STATE_NOSYNC; block_BytestreamEmpty( &p_sys->bytestream ); } date_Set( &p_sys->end_date, 0 ); block_Release( *pp_block ); return NULL; } if( !date_Get( &p_sys->end_date ) && (*pp_block)->i_pts <= VLC_TS_INVALID) { /* We've just started the stream, wait for the first PTS. */ block_Release( *pp_block ); return NULL; } block_BytestreamPush( &p_sys->bytestream, *pp_block ); while( 1 ) { switch( p_sys->i_state ) { case STATE_NOSYNC: while( block_PeekBytes( &p_sys->bytestream, p_header, 2 ) == VLC_SUCCESS ) { if( p_header[0] == 0x0b && p_header[1] == 0x77 ) { p_sys->i_state = STATE_SYNC; break; } block_SkipByte( &p_sys->bytestream ); } if( p_sys->i_state != STATE_SYNC ) { block_BytestreamFlush( &p_sys->bytestream ); /* Need more data */ return NULL; } case STATE_SYNC: /* New frame, set the Presentation Time Stamp */ p_sys->i_pts = p_sys->bytestream.p_block->i_pts; if( p_sys->i_pts > VLC_TS_INVALID && p_sys->i_pts != date_Get( &p_sys->end_date ) ) { date_Set( &p_sys->end_date, p_sys->i_pts ); } p_sys->i_state = STATE_HEADER; case STATE_HEADER: /* Get A/52 frame header (VLC_A52_HEADER_SIZE bytes) */ if( block_PeekBytes( &p_sys->bytestream, p_header, VLC_A52_HEADER_SIZE ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } /* Check if frame is valid and get frame info */ if( vlc_a52_header_Parse( &p_sys->frame, p_header, VLC_A52_HEADER_SIZE ) ) { msg_Dbg( p_dec, "emulated sync word" ); block_SkipByte( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; break; } p_sys->i_state = STATE_NEXT_SYNC; case STATE_NEXT_SYNC: /* TODO: If pp_block == NULL, flush the buffer without checking the * next sync word */ /* Check if next expected frame contains the sync word */ if( block_PeekOffsetBytes( &p_sys->bytestream, p_sys->frame.i_size, p_header, 2 ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } if( p_sys->b_packetizer && p_header[0] == 0 && p_header[1] == 0 ) { /* A52 wav files and audio CD's use stuffing */ p_sys->i_state = STATE_GET_DATA; break; } if( p_header[0] != 0x0b || p_header[1] != 0x77 ) { msg_Dbg( p_dec, "emulated sync word " "(no sync on following frame)" ); p_sys->i_state = STATE_NOSYNC; block_SkipByte( &p_sys->bytestream ); break; } p_sys->i_state = STATE_SEND_DATA; break; case STATE_GET_DATA: /* Make sure we have enough data. * (Not useful if we went through NEXT_SYNC) */ if( block_WaitBytes( &p_sys->bytestream, p_sys->frame.i_size ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } p_sys->i_state = STATE_SEND_DATA; case STATE_SEND_DATA: if( !(p_buf = GetOutBuffer( p_dec, &p_out_buffer )) ) { //p_dec->b_error = true; return NULL; } /* Copy the whole frame into the buffer. When we reach this point * we already know we have enough data available. */ block_GetBytes( &p_sys->bytestream, p_buf, __MIN( p_sys->frame.i_size, p_out_buffer->i_buffer ) ); /* Make sure we don't reuse the same pts twice */ if( p_sys->i_pts == p_sys->bytestream.p_block->i_pts ) p_sys->i_pts = p_sys->bytestream.p_block->i_pts = VLC_TS_INVALID; /* 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_out_buffer; } } }
/**************************************************************************** * 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; } } }
static block_t *PacketizeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; uint8_t p_header[VLC_DTS_HEADER_SIZE]; block_t *p_out_buffer; block_t *p_block = pp_block ? *pp_block : NULL; if( p_block ) { if ( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { /* First always drain complete blocks before discontinuity */ block_t *p_drain = PacketizeBlock( p_dec, NULL ); if(p_drain) return p_drain; PacketizeFlush( p_dec ); if ( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) { block_Release( p_block ); return NULL; } } if ( !date_Get( &p_sys->end_date ) && p_block->i_pts <= VLC_TS_INVALID ) { /* We've just started the stream, wait for the first PTS. */ block_Release( p_block ); return NULL; } block_BytestreamPush( &p_sys->bytestream, p_block ); } while( 1 ) { switch( p_sys->i_state ) { case STATE_NOSYNC: while( block_PeekBytes( &p_sys->bytestream, p_header, 6 ) == VLC_SUCCESS ) { if( vlc_dts_header_IsSync( p_header, 6 ) ) { p_sys->i_state = STATE_SYNC; break; } block_SkipByte( &p_sys->bytestream ); } if( p_sys->i_state != STATE_SYNC ) { block_BytestreamFlush( &p_sys->bytestream ); /* Need more data */ return NULL; } case STATE_SYNC: /* New frame, set the Presentation Time Stamp */ p_sys->i_pts = p_sys->bytestream.p_block->i_pts; if( p_sys->i_pts > VLC_TS_INVALID && p_sys->i_pts != date_Get( &p_sys->end_date ) ) { date_Set( &p_sys->end_date, p_sys->i_pts ); } p_sys->i_state = STATE_HEADER; case STATE_HEADER: /* Get DTS frame header (VLC_DTS_HEADER_SIZE bytes) */ if( block_PeekBytes( &p_sys->bytestream, p_header, VLC_DTS_HEADER_SIZE ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } /* Check if frame is valid and get frame info */ if( vlc_dts_header_Parse( &p_sys->dts, p_header, VLC_DTS_HEADER_SIZE ) != VLC_SUCCESS ) { msg_Dbg( p_dec, "emulated sync word" ); block_SkipByte( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; break; } if( p_sys->dts.b_substream ) { msg_Warn( p_dec, "substream without the paired core stream, " "skip it" ); p_sys->i_state = STATE_NOSYNC; if( block_SkipBytes( &p_sys->bytestream, p_sys->dts.i_frame_size ) != VLC_SUCCESS ) return NULL; break; } p_sys->i_input_size = p_sys->i_next_offset = p_sys->dts.i_frame_size; p_sys->i_state = STATE_NEXT_SYNC; case STATE_NEXT_SYNC: /* Check if next expected frame contains the sync word */ while( p_sys->i_state == STATE_NEXT_SYNC ) { if( block_PeekOffsetBytes( &p_sys->bytestream, p_sys->i_next_offset, p_header, VLC_DTS_HEADER_SIZE ) != VLC_SUCCESS ) { if( p_block == NULL ) /* drain */ { p_sys->i_state = STATE_GET_DATA; break; } /* Need more data */ return NULL; } if( p_header[0] == 0 ) { /* DTS wav files, audio CD's and some mkvs use stuffing */ p_sys->i_next_offset++; continue; } if( !vlc_dts_header_IsSync( p_header, VLC_DTS_HEADER_SIZE ) ) { msg_Dbg( p_dec, "emulated sync word " "(no sync on following frame)" ); p_sys->i_state = STATE_NOSYNC; block_SkipByte( &p_sys->bytestream ); break; } /* Check if a DTS substream packet is located just after * the core packet */ if( p_sys->i_next_offset == p_sys->dts.i_frame_size ) { vlc_dts_header_t next_header; if( vlc_dts_header_Parse( &next_header, p_header, VLC_DTS_HEADER_SIZE ) == VLC_SUCCESS && next_header.b_substream ) { p_sys->i_input_size += next_header.i_frame_size; } } p_sys->i_state = STATE_GET_DATA; } break; case STATE_GET_DATA: /* Make sure we have enough data. */ if( block_WaitBytes( &p_sys->bytestream, p_sys->i_input_size ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } p_sys->i_state = STATE_SEND_DATA; case STATE_SEND_DATA: if( !(p_out_buffer = GetOutBuffer( p_dec )) ) { return NULL; } /* Copy the whole frame into the buffer. When we reach this point * we already know we have enough data available. */ block_GetBytes( &p_sys->bytestream, p_out_buffer->p_buffer, p_out_buffer->i_buffer ); /* Make sure we don't reuse the same pts twice */ if( p_sys->i_pts == p_sys->bytestream.p_block->i_pts ) p_sys->i_pts = p_sys->bytestream.p_block->i_pts = VLC_TS_INVALID; if( p_sys->b_discontinuity ) { p_sys->b_discontinuity = false; p_out_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY; } /* So p_block doesn't get re-added several times */ if( pp_block ) *pp_block = block_BytestreamPop( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; return p_out_buffer; } } }
static block_t *PacketizeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; uint8_t p_header[VLC_A52_HEADER_SIZE]; block_t *p_out_buffer; block_t *p_block = pp_block ? *pp_block : NULL; if( p_block ) { if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED) ) { /* First always drain complete blocks before discontinuity */ block_t *p_drain = PacketizeBlock( p_dec, NULL ); if(p_drain) return p_drain; PacketizeFlush( p_dec ); if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) { block_Release( p_block ); return NULL; } } if( !date_Get( &p_sys->end_date ) && p_block->i_pts <= VLC_TS_INVALID) { /* We've just started the stream, wait for the first PTS. */ block_Release( p_block ); return NULL; } block_BytestreamPush( &p_sys->bytestream, p_block ); } while( 1 ) { switch( p_sys->i_state ) { case STATE_NOSYNC: while( block_PeekBytes( &p_sys->bytestream, p_header, 2 ) == VLC_SUCCESS ) { if( p_header[0] == 0x0b && p_header[1] == 0x77 ) { p_sys->i_state = STATE_SYNC; break; } block_SkipByte( &p_sys->bytestream ); } if( p_sys->i_state != STATE_SYNC ) { block_BytestreamFlush( &p_sys->bytestream ); /* Need more data */ return NULL; } case STATE_SYNC: /* New frame, set the Presentation Time Stamp */ p_sys->i_pts = p_sys->bytestream.p_block->i_pts; if( p_sys->i_pts > VLC_TS_INVALID && p_sys->i_pts != date_Get( &p_sys->end_date ) ) { date_Set( &p_sys->end_date, p_sys->i_pts ); } p_sys->i_state = STATE_HEADER; case STATE_HEADER: /* Get A/52 frame header (VLC_A52_HEADER_SIZE bytes) */ if( block_PeekBytes( &p_sys->bytestream, p_header, VLC_A52_HEADER_SIZE ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } /* Check if frame is valid and get frame info */ vlc_a52_header_t a52; if( vlc_a52_header_Parse( &a52, p_header, VLC_A52_HEADER_SIZE ) ) { msg_Dbg( p_dec, "emulated sync word" ); block_SkipByte( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; break; } if( a52.b_eac3 && a52.eac3.strmtyp != EAC3_STRMTYP_INDEPENDENT ) { /* Use the channel configuration of the independent stream */ p_sys->frame.i_samples = a52.i_samples; p_sys->frame.i_size = a52.i_size; } else p_sys->frame = a52; p_sys->i_state = STATE_NEXT_SYNC; case STATE_NEXT_SYNC: /* Check if next expected frame contains the sync word */ if( block_PeekOffsetBytes( &p_sys->bytestream, p_sys->frame.i_size, p_header, 2 ) != VLC_SUCCESS ) { if( p_block == NULL ) /* drain */ { p_sys->i_state = STATE_GET_DATA; break; } /* Need more data */ return NULL; } if( p_header[0] == 0 && p_header[1] == 0 ) { /* A52 wav files and audio CD's use stuffing */ p_sys->i_state = STATE_GET_DATA; break; } if( p_header[0] != 0x0b || p_header[1] != 0x77 ) { msg_Dbg( p_dec, "emulated sync word " "(no sync on following frame)" ); p_sys->i_state = STATE_NOSYNC; block_SkipByte( &p_sys->bytestream ); break; } p_sys->i_state = STATE_GET_DATA; break; case STATE_GET_DATA: /* Make sure we have enough data. */ if( block_WaitBytes( &p_sys->bytestream, p_sys->frame.i_size ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } p_sys->i_state = STATE_SEND_DATA; case STATE_SEND_DATA: if( !(p_out_buffer = GetOutBuffer( p_dec )) ) { return NULL; } /* Copy the whole frame into the buffer. When we reach this point * we already know we have enough data available. */ block_GetBytes( &p_sys->bytestream, p_out_buffer->p_buffer, __MIN( p_sys->frame.i_size, p_out_buffer->i_buffer ) ); /* Make sure we don't reuse the same pts twice */ if( p_sys->i_pts == p_sys->bytestream.p_block->i_pts ) p_sys->i_pts = p_sys->bytestream.p_block->i_pts = VLC_TS_INVALID; if( p_sys->b_discontuinity ) { p_out_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY; p_sys->b_discontuinity = false; } /* So p_block doesn't get re-added several times */ if( pp_block ) *pp_block = block_BytestreamPop( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; return p_out_buffer; } } }
/***************************************************************************** * 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; } } }
/**************************************************************************** * 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; } } }