/*****************************************************************************
* h264.c: h264/avc video packetizer
*****************************************************************************
- * Copyright (C) 2001, 2002, 2006 the VideoLAN team
+ * Copyright (C) 2001, 2002, 2006 VLC authors and VideoLAN
* $Id$
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin@videolan.org>
* Derk-Jan Hartman <hartman at videolan dot org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include <assert.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_bits.h>
#include "../codec/cc.h"
#include "packetizer_helper.h"
+#include "../demux/mpeg/mpeg_parser_helpers.h"
/*****************************************************************************
* Module descriptor
/* */
bool b_slice;
block_t *p_frame;
+ bool b_frame_sps;
+ bool b_frame_pps;
bool b_header;
bool b_sps;
bool b_pps;
block_t *pp_sps[SPS_MAX];
block_t *pp_pps[PPS_MAX];
+ int i_recovery_frames; /* -1 = no recovery */
/* avcC data */
int i_avcC_length_size;
/* ref_idc == 0 for 6,9,10,11,12 */
};
-enum nal_priority_e
+/* Defined in H.264 annex D */
+enum sei_type_e
{
- NAL_PRIORITY_DISPOSABLE = 0,
- NAL_PRIORITY_LOW = 1,
- NAL_PRIORITY_HIGH = 2,
- NAL_PRIORITY_HIGHEST = 3,
+ SEI_USER_DATA_REGISTERED = 4,
+ SEI_RECOVERY_POINT = 6
};
#define BLOCK_FLAG_PRIVATE_AUD (1 << BLOCK_FLAG_PRIVATE_SHIFT)
static block_t *PacketizeParse( void *p_private, bool *pb_ts_used, block_t * );
static int PacketizeValidate( void *p_private, block_t * );
-static block_t *ParseNALBlock( decoder_t *, bool *pb_used_ts, block_t * );
+static block_t *ParseNALBlock( decoder_t *, bool *pb_ts_used, block_t * );
static block_t *CreateAnnexbNAL( decoder_t *, const uint8_t *p, int );
static block_t *OutputPicture( decoder_t *p_dec );
packetizer_Init( &p_sys->packetizer,
p_h264_startcode, sizeof(p_h264_startcode),
- p_h264_startcode, 1,
+ p_h264_startcode, 1, 5,
PacketizeReset, PacketizeParse, PacketizeValidate, p_dec );
p_sys->b_slice = false;
p_sys->p_frame = NULL;
+ p_sys->b_frame_sps = false;
+ p_sys->b_frame_pps = false;
+
p_sys->b_header= false;
p_sys->b_sps = false;
p_sys->b_pps = false;
p_sys->pp_sps[i] = NULL;
for( i = 0; i < PPS_MAX; i++ )
p_sys->pp_pps[i] = NULL;
+ p_sys->i_recovery_frames = -1;
p_sys->slice.i_nal_type = -1;
p_sys->slice.i_nal_ref_idc = -1;
if( p_sys->cc.i_data <= 0 )
return NULL;
- p_cc = block_New( p_dec, p_sys->cc.i_data);
+ 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 );
if( p_sys->p_frame )
block_ChainRelease( p_sys->p_frame );
p_sys->p_frame = NULL;
+ p_sys->b_frame_sps = false;
+ p_sys->b_frame_pps = false;
p_sys->slice.i_frame_type = 0;
p_sys->b_slice = false;
}
decoder_t *p_dec = p_private;
/* Remove trailing 0 bytes */
- while( p_block->i_buffer && p_block->p_buffer[p_block->i_buffer-1] == 0x00 )
+ while( p_block->i_buffer > 5 && p_block->p_buffer[p_block->i_buffer-1] == 0x00 )
p_block->i_buffer--;
return ParseNALBlock( p_dec, pb_ts_used, p_block );
{
block_t *p_nal;
- p_nal = block_New( p_dec, 4 + i_size );
+ p_nal = block_Alloc( 4 + i_size );
if( !p_nal ) return NULL;
/* Add start code */
*pp_ret = dst;
if( dst )
- {
- while( src < end )
- {
- if( src < end - 3 && src[0] == 0x00 && src[1] == 0x00 &&
- src[2] == 0x03 )
- {
- *dst++ = 0x00;
- *dst++ = 0x00;
-
- src += 3;
- continue;
- }
- *dst++ = *src++;
- }
- }
- *pi_ret = dst - *pp_ret;
-}
-
-static inline int bs_read_ue( bs_t *s )
-{
- int i = 0;
-
- while( bs_read1( s ) == 0 && s->p < s->p_end && i < 32 )
- {
- i++;
- }
- return( ( 1 << i) - 1 + bs_read( s, i ) );
-}
-
-static inline int bs_read_se( bs_t *s )
-{
- int val = bs_read_ue( s );
-
- return val&0x01 ? (val+1)/2 : -(val/2);
+ *pi_ret = nal_decode(src, dst, i_src);
}
/*****************************************************************************
* 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_used_ts, block_t *p_frag )
+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;
/* 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_slice )
p_pic = OutputPicture( p_dec );
+ p_sys->b_frame_sps = true;
PutSPS( p_dec, p_frag );
{
if( p_sys->b_slice )
p_pic = OutputPicture( p_dec );
+ p_sys->b_frame_pps = true;
PutPPS( p_dec, p_frag );
if( p_frag )
block_ChainAppend( &p_sys->p_frame, p_frag );
- *pb_used_ts = false;
- if( p_sys->i_frame_dts <= VLC_TS_INVALID &&
+ *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_used_ts = true;
+ *pb_ts_used = true;
}
return p_pic;
}
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_pic;
- if( !p_sys->b_header && p_sys->slice.i_frame_type != BLOCK_FLAG_TYPE_I)
+ 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;
- if( p_sys->slice.i_frame_type == BLOCK_FLAG_TYPE_I && p_sys->b_sps && p_sys->b_pps )
+ 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 )
}
block_t *p_list = NULL;
- for( int i = 0; i < SPS_MAX; i++ )
+ 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; 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( p_list )
+ if( b_sps_pps_i && p_list )
p_sys->b_header = true;
if( p_head )
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_dts = p_pic->i_dts;
p_sys->i_cc_flags = p_pic->i_flags;
- /* Swap cc buffer */
- cc_data_t cc_tmp = p_sys->cc;
p_sys->cc = p_sys->cc_next;
- p_sys->cc_next = cc_tmp;
-
cc_Flush( &p_sys->cc_next );
return p_pic;
p_dec->fmt_out.i_level = bs_read( &s, 8 );
/* sps id */
i_sps_id = bs_read_ue( &s );
- if( i_sps_id >= SPS_MAX )
+ if( i_sps_id >= SPS_MAX || i_sps_id < 0 )
{
msg_Warn( p_dec, "invalid SPS (sps_id=%d)", i_sps_id );
free( pb_dec );
/* chroma_format_idc */
const int i_chroma_format_idc = bs_read_ue( &s );
if( i_chroma_format_idc == 3 )
- bs_skip( &s, 1 ); /* seperate_colour_plane_flag */
+ bs_skip( &s, 1 ); /* separate_colour_plane_flag */
/* bit_depth_luma_minus8 */
bs_read_ue( &s );
/* bit_depth_chroma_minus8 */
/* b_frame_mbs_only */
p_sys->b_frame_mbs_only = bs_read( &s, 1 );
+ p_dec->fmt_out.video.i_height *= ( 2 - p_sys->b_frame_mbs_only );
if( p_sys->b_frame_mbs_only == 0 )
{
bs_skip( &s, 1 );
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *pb_dec;
int i_dec;
- int i_first_mb, i_slice_type;
+ int i_slice_type;
slice_t slice;
bs_t s;
bs_init( &s, pb_dec, i_dec );
/* first_mb_in_slice */
- i_first_mb = bs_read_ue( &s );
+ /* int i_first_mb = */ bs_read_ue( &s );
/* slice_type */
switch( (i_slice_type = bs_read_ue( &s )) )
break;
/* Look for user_data_registered_itu_t_t35 */
- if( i_type == 4 )
+ if( i_type == SEI_USER_DATA_REGISTERED )
{
static const uint8_t p_dvb1_data_start_code[] = {
0xb5,
cc_Extract( &p_sys->cc_next, true, &p_t35[3], i_t35 - 3 );
}
}
+
+ /* Look for SEI recovery point */
+ if( i_type == SEI_RECOVERY_POINT )
+ {
+ bs_t s;
+ const int i_rec = i_size;
+ const uint8_t *p_rec = &pb_dec[i_used];
+
+ bs_init( &s, p_rec, i_rec );
+ int i_recovery_frames = bs_read_ue( &s );
+ //bool b_exact_match = bs_read( &s, 1 );
+ //bool b_broken_link = bs_read( &s, 1 );
+ //int i_changing_slice_group = bs_read( &s, 2 );
+ if( !p_sys->b_header )
+ {
+ msg_Dbg( p_dec, "Seen SEI recovery point, %d recovery frames", i_recovery_frames );
+ if ( p_sys->i_recovery_frames == -1 || i_recovery_frames < p_sys->i_recovery_frames )
+ p_sys->i_recovery_frames = i_recovery_frames;
+ }
+ }
+
i_used += i_size;
}