+void x264_sei_buffering_period_write( x264_t *h, bs_t *s )
+{
+ x264_sps_t *sps = h->sps;
+ bs_t q;
+ ALIGNED_4( uint8_t tmp_buf[100] );
+ M32( tmp_buf ) = 0; // shut up gcc
+ bs_init( &q, tmp_buf, 100 );
+
+ bs_realign( &q );
+ bs_write_ue( &q, sps->i_id );
+
+ if( sps->vui.b_nal_hrd_parameters_present )
+ {
+ bs_write( &q, sps->vui.hrd.i_initial_cpb_removal_delay_length, h->initial_cpb_removal_delay );
+ bs_write( &q, sps->vui.hrd.i_initial_cpb_removal_delay_length, h->initial_cpb_removal_delay_offset );
+ }
+
+ bs_align_10( &q );
+ bs_flush( &q );
+
+ x264_sei_write( s, tmp_buf, bs_pos( &q ) / 8, SEI_BUFFERING_PERIOD );
+}
+
+void x264_sei_pic_timing_write( x264_t *h, bs_t *s )
+{
+ x264_sps_t *sps = h->sps;
+ bs_t q;
+ ALIGNED_4( uint8_t tmp_buf[100] );
+ M32( tmp_buf ) = 0; // shut up gcc
+ bs_init( &q, tmp_buf, 100 );
+
+ bs_realign( &q );
+
+ if( sps->vui.b_nal_hrd_parameters_present || sps->vui.b_vcl_hrd_parameters_present )
+ {
+ bs_write( &q, sps->vui.hrd.i_cpb_removal_delay_length, h->fenc->i_cpb_delay - h->i_cpb_delay_pir_offset );
+ bs_write( &q, sps->vui.hrd.i_dpb_output_delay_length, h->fenc->i_dpb_output_delay );
+ }
+
+ if( sps->vui.b_pic_struct_present )
+ {
+ bs_write( &q, 4, h->fenc->i_pic_struct-1 ); // We use index 0 for "Auto"
+
+ // These clock timestamps are not standardised so we don't set them
+ // They could be time of origin, capture or alternative ideal display
+ for( int i = 0; i < num_clock_ts[h->fenc->i_pic_struct]; i++ )
+ bs_write1( &q, 0 ); // clock_timestamp_flag
+ }
+
+ bs_align_10( &q );
+ bs_flush( &q );
+
+ x264_sei_write( s, tmp_buf, bs_pos( &q ) / 8, SEI_PIC_TIMING );
+}
+
+void x264_sei_frame_packing_write( x264_t *h, bs_t *s )
+{
+ int quincunx_sampling_flag = h->param.i_frame_packing == 0;
+ bs_t q;
+ ALIGNED_4( uint8_t tmp_buf[100] );
+ M32( tmp_buf ) = 0; // shut up gcc
+ bs_init( &q, tmp_buf, 100 );
+
+ bs_realign( &q );
+
+ bs_write_ue( &q, 0 ); // frame_packing_arrangement_id
+ bs_write1( &q, 0 ); // frame_packing_arrangement_cancel_flag
+ bs_write ( &q, 7, h->param.i_frame_packing ); // frame_packing_arrangement_type
+ bs_write1( &q, quincunx_sampling_flag ); // quincunx_sampling_flag
+
+ // 0: views are unrelated, 1: left view is on the left, 2: left view is on the right
+ bs_write ( &q, 6, h->param.i_frame_packing != 6 ); // content_interpretation_type
+
+ bs_write1( &q, 0 ); // spatial_flipping_flag
+ bs_write1( &q, 0 ); // frame0_flipped_flag
+ bs_write1( &q, 0 ); // field_views_flag
+ bs_write1( &q, h->param.i_frame_packing == 5 && !(h->fenc->i_frame&1) ); // current_frame_is_frame0_flag
+ bs_write1( &q, 0 ); // frame0_self_contained_flag
+ bs_write1( &q, 0 ); // frame1_self_contained_flag
+ if ( quincunx_sampling_flag == 0 && h->param.i_frame_packing != 5 )
+ {
+ bs_write( &q, 4, 0 ); // frame0_grid_position_x
+ bs_write( &q, 4, 0 ); // frame0_grid_position_y
+ bs_write( &q, 4, 0 ); // frame1_grid_position_x
+ bs_write( &q, 4, 0 ); // frame1_grid_position_y
+ }
+ bs_write( &q, 8, 0 ); // frame_packing_arrangement_reserved_byte
+ // "frame_packing_arrangement_repetition_period equal to 1 specifies that the frame packing arrangement SEI message persists in output"
+ // for (i_frame_packing == 5) this will undermine current_frame_is_frame0_flag which must alternate every view sequence
+ bs_write_ue( &q, h->param.i_frame_packing != 5 ); // frame_packing_arrangement_repetition_period
+ bs_write1( &q, 0 ); // frame_packing_arrangement_extension_flag
+
+ bs_align_10( &q );
+ bs_flush( &q );
+
+ x264_sei_write( s, tmp_buf, bs_pos( &q ) / 8, SEI_FRAME_PACKING );
+}
+
+void x264_filler_write( x264_t *h, bs_t *s, int filler )
+{
+ bs_realign( s );
+
+ for( int i = 0; i < filler; i++ )
+ bs_write( s, 8, 0xff );
+
+ bs_rbsp_trailing( s );
+ bs_flush( s );
+}
+
+void x264_sei_dec_ref_pic_marking_write( x264_t *h, bs_t *s )
+{
+ x264_slice_header_t *sh = &h->sh_backup;
+ bs_t q;
+ ALIGNED_4( uint8_t tmp_buf[100] );
+ M32( tmp_buf ) = 0; // shut up gcc
+ bs_init( &q, tmp_buf, 100 );
+
+ bs_realign( &q );
+
+ /* We currently only use this for repeating B-refs, as required by Blu-ray. */
+ bs_write1( &q, 0 ); //original_idr_flag
+ bs_write_ue( &q, sh->i_frame_num ); //original_frame_num
+ if( !h->sps->b_frame_mbs_only )
+ bs_write1( &q, 0 ); //original_field_pic_flag
+
+ bs_write1( &q, sh->i_mmco_command_count > 0 );
+ if( sh->i_mmco_command_count > 0 )
+ {
+ for( int i = 0; i < sh->i_mmco_command_count; i++ )
+ {
+ bs_write_ue( &q, 1 );
+ bs_write_ue( &q, sh->mmco[i].i_difference_of_pic_nums - 1 );
+ }
+ bs_write_ue( &q, 0 );
+ }
+
+ bs_align_10( &q );
+ bs_flush( &q );
+
+ x264_sei_write( s, tmp_buf, bs_pos( &q ) / 8, SEI_DEC_REF_PIC_MARKING );
+}
+
+int x264_sei_avcintra_umid_write( x264_t *h, bs_t *s )
+{
+ uint8_t data[512];
+ const char *msg = "UMID";
+ const int len = 497;
+
+ memset( data, 0xff, len );
+ memcpy( data, avcintra_uuid, sizeof(avcintra_uuid) );
+ memcpy( data+16, msg, strlen(msg) );
+
+ data[20] = 0x13;
+ /* These bytes appear to be some sort of frame/seconds counter in certain applications,
+ * but others jump around, so leave them as zero for now */
+ data[22] = data[23] = data[25] = data[26] = 0;
+ data[28] = 0x14;
+ data[30] = data[31] = data[33] = data[34] = 0;
+ data[36] = 0x60;
+ data[41] = 0x22; /* Believed to be some sort of end of basic UMID identifier */
+ data[60] = 0x62;
+ data[62] = data[63] = data[65] = data[66] = 0;
+ data[68] = 0x63;
+ data[70] = data[71] = data[73] = data[74] = 0;
+
+ x264_sei_write( &h->out.bs, data, len, SEI_USER_DATA_UNREGISTERED );
+
+ return 0;
+}
+
+int x264_sei_avcintra_vanc_write( x264_t *h, bs_t *s, int len )
+{
+ uint8_t data[6000];
+ const char *msg = "VANC";
+ if( len > sizeof(data) )
+ {
+ x264_log( h, X264_LOG_ERROR, "AVC-Intra SEI is too large (%d)\n", len );
+ return -1;
+ }
+
+ memset( data, 0xff, len );
+ memcpy( data, avcintra_uuid, sizeof(avcintra_uuid) );
+ memcpy( data+16, msg, strlen(msg) );
+
+ x264_sei_write( &h->out.bs, data, len, SEI_USER_DATA_UNREGISTERED );
+
+ return 0;
+}
+