/* libfame - Fast Assembly MPEG Encoder Library Copyright (C) 2000-2001 Vivien Chappelier Thomas Cougnard This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "fame.h" #include "fame_malloc.h" #include "fame_syntax.h" #include "fame_bitbuffer.h" #include "fame_syntax_mpeg4.h" #include "table_zigzag_mpeg4.h" #include "table_rlehuff_mpeg4.h" #include "table_dc_mpeg4.h" #include "table_cae.h" #include "table_quant_mpeg4.h" #include "table_cbp_mpeg4.h" #include "table_mv.h" #include "table_clip_mpeg4.h" #if defined(HAS_MMX) #include "ac_mmx.h" #else #include "ac_int.h" #endif #define OPENDIVX_COMPATIBILITY /* needed to output OpenDivX readable output */ /* prediction direction */ #define LEFT_PREDICTED 0 #define TOP_PREDICTED 1 /* maximum size of arithmetic coded sequence */ #define CAE_SEQUENCE_LENGTH 4096 #define CAE_MAX_HEADING 3 #define CAE_MAX_MIDDLE 10 #define CAE_MAX_TRAILING 2 #define CAE_1_2 0x80000000 #define CAE_1_4 0x40000000 /* The header codes */ #define MPEG4_SEQUENCE_START_CODE 0x1b0 #define MPEG4_SEQUENCE_END_CODE 0x1b1 #define MPEG4_VISUAL_OBJ_START_CODE 0x1b5 #define MPEG4_VIDEO_OBJ_START_CODE 0x100 #define MPEG4_VIDEO_OBJ_LAYER_START_CODE 0x120 #define MPEG4_GVOP_START_CODE 0x1b3 #define MPEG4_VOP_START_CODE 0x1b6 #define MPEG4_SLICE_BASE_CODE 0x100 /* Visual Object Type */ #define MPEG4_Visual_Object_Type_VideoID 0x01 #define MPEG4_Visual_Object_Type_still_textureID 0x02 #define MPEG4_Visual_Object_Type_meshID 0x03 #define MPEG4_Visual_Object_Type_FBA_ID 0x04 #define MPEG4_Visual_Object_Type_3D_meshID 0x05 /* Video Object type */ #define MPEG4_Video_Object_Type_SimpleObject 0x01 /* ... */ #define MPEG4_Video_Object_Type_FineGranularityScalable 0x12 /* Aspect Ratio Info */ #define MPEG4_Aspect_Ratio_Info_Square 0x01 #define MPEG4_Aspect_Ratio_Info_ExtendedPAR 0x0A /* Video Object layer Shape */ #define MPEG4_Video_Object_Layer_Shape_Rectangular 0x00 #define MPEG4_Video_Object_Layer_Shape_Binary 0x01 #define MPEG4_Video_Object_Layer_Shape_BinaryOnly 0x02 #define MPEG4_Video_Object_Layer_Shape_Grayscale 0x03 /* VOP Coding Type */ #define MPEG4_I_FRAME 0 #define MPEG4_P_FRAME 1 #define MPEG4_B_FRAME 2 /* The fixed values */ #define MPEG4_PROFILE_AND_LEVEL 0x01 #define MPEG4_IS_VISUAL_OBJ_IDENT 0x00 #define MPEG4_VISUAL_OBJ_TYPE 0x01 #define MPEG4_VIDEO_SIGNAL_TYPE 0x00 #define MPEG4_IS_OBJ_LAYER_IDENT 0x00 #define MPEG4_VOL_SHAPE_RECTANGULAR 0x00 #define MPEG4_VOL_SHAPE_BINARY_ONLY 0x02 #define MPEG4_VOP_TIME_INCR_RES 0x000F /* ?? */ static void mpeg4_init(fame_syntax_t *syntax, int mb_width, int mb_height, unsigned char **intra_default_matrix, unsigned char **inter_default_matrix, unsigned char *intra_dc_y_scale_table, unsigned char *intra_dc_c_scale_table, fame_mismatch_t *mismatch_type, unsigned int flags); static void mpeg4_close(fame_syntax_t *syntax); static void mpeg4_use(fame_syntax_t *syntax, unsigned char *buffer, int size); static int mpeg4_flush(fame_syntax_t *syntax); static void mpeg4_start_sequence(fame_syntax_t *syntax, int width, int height, int fps_num, int fps_den, int size, int bitrate); static void mpeg4_start_GOP(fame_syntax_t *syntax, int frame); static void mpeg4_start_picture(fame_syntax_t *syntax, char frame_type, int frame_number, fame_box_t *box, int rounding_control, int search_range); static void mpeg4_start_slice(fame_syntax_t *syntax, int vpos, int length, unsigned char qscale); static void mpeg4_end_slice(fame_syntax_t *syntax); static void mpeg4_end_sequence(fame_syntax_t *syntax); static void mpeg4_predict_vector(fame_syntax_t *syntax, int mb_x, int mb_y, int k, fame_motion_vector_t *mv); static void mpeg4_compute_chrominance_vectors(fame_syntax_t *syntax, fame_motion_vector_t *vectors, unsigned char pattern); static int mpeg4_write_intra_mb(fame_syntax_t *syntax, int mb_x, int mb_y, short *blocks[6], unsigned char *bab, unsigned char *bab_map, fame_bab_t bab_type, int dquant, unsigned char pattern); static int mpeg4_write_inter_mb(fame_syntax_t *syntax, int mb_x, int mb_y, short *blocks[6], unsigned char *bab, unsigned char *bab_map, fame_bab_t bab_type, int dquant, unsigned char pattern, fame_motion_vector_t *forward, fame_motion_vector_t *backward, fame_motion_coding_t motion_coding); FAME_CONSTRUCTOR(fame_syntax_mpeg4_t) { FAME_OBJECT(this)->name = "MPEG-4 bitstream syntax"; FAME_SYNTAX(this)->init = mpeg4_init; FAME_SYNTAX(this)->use = mpeg4_use; FAME_SYNTAX(this)->flush = mpeg4_flush; FAME_SYNTAX(this)->start_sequence = mpeg4_start_sequence; FAME_SYNTAX(this)->start_GOP = mpeg4_start_GOP; FAME_SYNTAX(this)->start_picture = mpeg4_start_picture; FAME_SYNTAX(this)->start_slice = mpeg4_start_slice; FAME_SYNTAX(this)->end_slice = mpeg4_end_slice; FAME_SYNTAX(this)->end_sequence = mpeg4_end_sequence; FAME_SYNTAX(this)->predict_vector = mpeg4_predict_vector; FAME_SYNTAX(this)->compute_chrominance_vectors = mpeg4_compute_chrominance_vectors; FAME_SYNTAX(this)->write_intra_mb = mpeg4_write_intra_mb; FAME_SYNTAX(this)->write_inter_mb = mpeg4_write_inter_mb; FAME_SYNTAX(this)->close = mpeg4_close; return(this); } static const int bab_type_intra_context_weight[4] = { 1, 3, 9, 27 }; static const int bab_type_intra_vl[81][3] = { { 1, 3, 2 }, { 3, 2, 1 }, { 2, 3, 1 }, { 1, 3, 2 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 3, 2 }, { 1, 2, 3 }, { 2, 3, 1 }, { 3, 2, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, { 3, 2, 1 }, { 1, 2, 3 }, { 2, 1, 3 }, { 3, 2, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, { 2, 3, 1 }, { 1, 2, 3 }, { 3, 2, 1 }, { 2, 3, 1 }, { 1, 2, 3 }, { 3, 2, 1 }, { 2, 3, 1 }, { 3, 2, 1 }, { 3, 2, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 3, 2, 1 }, { 1, 2, 3 }, { 3, 2, 1 }, { 3, 2, 1 }, { 1, 2, 3 }, { 3, 1, 2 }, { 3, 2, 1 }, { 1, 2, 3 }, { 3, 1, 2 }, { 3, 2, 1 }, { 1, 2, 3 }, { 3, 2, 1 }, { 2, 3, 1 }, { 1, 2, 3 }, { 3, 2, 1 }, { 2, 3, 1 }, { 1, 3, 2 }, { 3, 1, 2 }, { 2, 3, 1 }, { 1, 3, 2 }, { 2, 3, 1 }, { 2, 3, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 2, 3, 1 }, { 1, 2, 3 }, { 3, 2, 1 }, { 3, 2, 1 }, { 2, 3, 1 }, { 3, 1, 2 }, { 3, 1, 2 }, { 2, 3, 1 }, { 3, 1, 2 }, { 3, 2, 1 }, { 1, 3, 2 }, { 3, 2, 1 }, { 2, 3, 1 }, { 2, 3, 1 }, { 3, 1, 2 }, { 3, 2, 1 }, { 1, 3, 2 }, { 3, 1, 2 }, { 3, 2, 1 } }; static int inline get_min_bit(unsigned int n) { unsigned int i=0; while (n) { i++; n>>=1; } return i; } static void inline mpeg4_init_vlc_tables(fame_vlc_t *intra, fame_vlc_t *inter) { int z, l; int level, run, last, t; for(t = 0; t < 2; t++) { fame_vlc_t *vlc; /* level = [-255;255], run = [0;64], last = [0;1] */ if(t == 0) vlc = intra; else vlc = inter; for(last = 0; last < 2; last++) { char *max_level, *max_run; fame_vlc_t **table; if(t == 0) { if(last == 0) { max_level = rlehuff_intra_max_level; max_run = rlehuff_intra_max_run; table = rlehuff_intra_notlast_vlc; } else { max_level = rlehuff_intra_max_level_last; max_run = rlehuff_intra_max_run_last; table = rlehuff_intra_last_vlc; } } else { if(last == 0) { max_level = rlehuff_inter_max_level; max_run = rlehuff_inter_max_run; table = rlehuff_inter_notlast_vlc; } else { max_level = rlehuff_inter_max_level_last; max_run = rlehuff_inter_max_run_last; table = rlehuff_inter_last_vlc; } } for(l = -255; l < 256; l++) { for(z = 0; z < 64; z++) { level = l; run = z; if(abs(level) <= max_level[run] && run <= max_run[abs(level)]) { /* vlc */ vlc->code = table[run][level].code; vlc->length = table[run][level].length; } else { /* reduce level */ if(level > 0) level -= max_level[run]; else level += max_level[run]; if(abs(level) <= max_level[run] && run <= max_run[abs(level)]) { /* escape + 1 + vlc */ vlc->code = (0x06 << table[run][level].length) | table[run][level].code; vlc->length = table[run][level].length + 8; } else { /* restore level */ if(level > 0) level += max_level[run]; else level -= max_level[run]; /* reduce run */ run -= max_run[abs(level)] + 1; if(abs(level) <= max_level[run] && run <= max_run[abs(level)]) { /* escape + 01 + vlc */ vlc->code = (0x0e << table[run][level].length) | table[run][level].code; vlc->length = table[run][level].length + 9; } else { /* restore run */ run += max_run[abs(level)] + 1; /* escape + 00 + last + run + level */ vlc->code = (unsigned long) ((0x1e + last) << 20) | (z << 14) | (1 << 13) | ((l & 0xfff) << 1) | 1; vlc->length = 30; } } } vlc++; } } } } } static void inline mpeg4_init_symbol(int *symbol) { int i; for(i = 0; i < 2048; i++) { if(i & 1) { if(cae_intra_prob[i >> 1] > 32768) symbol[i] = 65536 - cae_intra_prob[i >> 1]; /* > 0 : LPS */ else symbol[i] = -cae_intra_prob[i >> 1]; /* < 0 : MPS */ } else { if(cae_intra_prob[i >> 1] > 32768) symbol[i] = -(65536 - cae_intra_prob[i >> 1]); /* < 0 : MPS */ else symbol[i] = cae_intra_prob[i >> 1]; /* > 0 : LPS */ } } } static void inline mpeg4_next_start_code(fame_bitbuffer_t *buffer) { bitbuffer_write(buffer, 0, 1); if(bitbuffer_padding(buffer) != 0) bitbuffer_write(buffer, ((1 << bitbuffer_padding(buffer)) - 1), bitbuffer_padding(buffer)); } /* The default intra quantisation table */ static void mpeg4_init(fame_syntax_t *syntax, int mb_width, int mb_height, unsigned char **intra_matrix, unsigned char **inter_matrix, unsigned char *intra_dc_y_scale_table, unsigned char *intra_dc_c_scale_table, fame_mismatch_t *mismatch_type, unsigned int flags) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); int i; int qscale; syntax_mpeg4->mb_width = mb_width; syntax_mpeg4->mb_height = mb_height; syntax_mpeg4->motion_pred = (fame_motion_vector_t *) fame_malloc(4*mb_width*mb_height*sizeof(fame_motion_vector_t)); syntax_mpeg4->cae_h = (fame_syntax_cae_t *) fame_malloc(sizeof(fame_syntax_cae_t)); syntax_mpeg4->cae_v = (fame_syntax_cae_t *) fame_malloc(sizeof(fame_syntax_cae_t)); syntax_mpeg4->cae_h->buffer = fame_malloc(CAE_SEQUENCE_LENGTH); syntax_mpeg4->cae_v->buffer = fame_malloc(CAE_SEQUENCE_LENGTH); /* alloc one line of blocks for Y, Cr, Cb vertical AC/DC predictors*/ /* 15 coefficients need to be kept (7+7 AC + 1 DC) for each predictor */ /* 16 are allocated for alignement. */ syntax_mpeg4->y_pred_v[0]=(short int **)fame_malloc(mb_width*sizeof(short int *)); syntax_mpeg4->y_pred_v[1]=(short int **)fame_malloc(mb_width*sizeof(short int *)); syntax_mpeg4->cr_pred_v = (short int **)fame_malloc(mb_width*sizeof(short int *)); syntax_mpeg4->cb_pred_v = (short int **)fame_malloc(mb_width*sizeof(short int *)); for(i = 0; i < mb_width; i++) { syntax_mpeg4->y_pred_v[0][i] = (short int *)fame_malloc(16 * sizeof(short int)); syntax_mpeg4->y_pred_v[1][i] = (short int *)fame_malloc(16 * sizeof(short int)); syntax_mpeg4->cr_pred_v[i] = (short int *)fame_malloc(16 * sizeof(short int)); syntax_mpeg4->cb_pred_v[i] = (short int *)fame_malloc(16 * sizeof(short int)); } syntax_mpeg4->y_pred_h[0] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->y_pred_h[1] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->y_pred_h[2] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->cr_pred_h[0] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->cr_pred_h[1] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->cb_pred_h[0] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->cb_pred_h[1] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->pred[0] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->pred[1] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->pred[2] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->pred[3] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->pred[4] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->pred[5] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->diff[0] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->diff[1] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->diff[2] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->diff[3] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->diff[4] = (short int *) fame_malloc(16 * sizeof(short int)); syntax_mpeg4->diff[5] = (short int *) fame_malloc(16 * sizeof(short int)); /* compute default DC predictor values */ syntax_mpeg4->pred_default = (short int *) fame_malloc(16 * sizeof(short int)); memset(syntax_mpeg4->pred_default, 0, 16 * sizeof(short int)); syntax_mpeg4->pred_default[0] = 1024; syntax_mpeg4->fps_num = 25; syntax_mpeg4->fps_den = 1; /* initialize vlc tables */ syntax_mpeg4->intra_table = (fame_vlc_t *) fame_malloc(2*64*511*sizeof(fame_vlc_t)); syntax_mpeg4->inter_table = (fame_vlc_t *) fame_malloc(2*64*511*sizeof(fame_vlc_t)); mpeg4_init_vlc_tables(syntax_mpeg4->intra_table, syntax_mpeg4->inter_table); /* center vlc tables */ syntax_mpeg4->intra_table += 64*255; syntax_mpeg4->inter_table += 64*255; /* Initialization of default values */ /* Video Object Sequence */ syntax_mpeg4->profile_and_level_indication = 0x01; /* Visual Object */ syntax_mpeg4->is_visual_object_identifier = 0; syntax_mpeg4->visual_object_type = MPEG4_Visual_Object_Type_VideoID ; syntax_mpeg4->visual_object_verid = 1; syntax_mpeg4->visual_object_priority = 1; syntax_mpeg4->visual_object_type = 1; /* video ID */ /* Video Signal Type */ syntax_mpeg4->video_signal_type = 0; /* not present */ syntax_mpeg4->video_format = 5; /* unspecified */ syntax_mpeg4->video_range = 0; syntax_mpeg4->colour_description = 0; /* not present */ syntax_mpeg4->colour_primaries = 2; /* unknown */ syntax_mpeg4->transfer_characteristics = 2; /* unknown */ syntax_mpeg4->matrix_coefficients = 2; /* unknown */ /* assign values depending on flags */ if(flags & FAME_SYNTAX_ARBITRARY_SHAPE) syntax_mpeg4->video_object_layer_shape = MPEG4_Video_Object_Layer_Shape_Binary; else syntax_mpeg4->video_object_layer_shape = MPEG4_Video_Object_Layer_Shape_Rectangular; if(flags & FAME_SYNTAX_LOSSLESS_SHAPE) syntax_mpeg4->change_conv_ratio_disable=1; else syntax_mpeg4->change_conv_ratio_disable=0; /* fill in quantization tables */ if(*intra_matrix) { int i; syntax_mpeg4->load_intra_quant_mat=1; for(i = 0; i < 64; i++) syntax_mpeg4->intra_quant_mat[i] = (*intra_matrix)[mpeg4_zigzag_table[i]]; } else { syntax_mpeg4->load_intra_quant_mat=0; *intra_matrix = mpeg4_intra_quantisation_table; } if(*inter_matrix) { int i; syntax_mpeg4->load_nonintra_quant_mat=1; for(i = 0; i < 64; i++) syntax_mpeg4->nonintra_quant_mat[i] = (*inter_matrix)[mpeg4_zigzag_table[i]]; } else { syntax_mpeg4->load_nonintra_quant_mat=0; *inter_matrix = mpeg4_inter_quantisation_table; } /* fill in intra DC quantizer values */ for(qscale = 0; qscale < 32; qscale++) { if(qscale >= 1 && qscale <= 4) { syntax_mpeg4->y_dc_scaler[qscale] = 8; syntax_mpeg4->c_dc_scaler[qscale] = 8; } if(qscale >= 5 && qscale <= 8) { syntax_mpeg4->y_dc_scaler[qscale] = qscale << 1; syntax_mpeg4->c_dc_scaler[qscale] = (qscale+13) >> 1; } if(qscale >= 9 && qscale <= 24) { syntax_mpeg4->y_dc_scaler[qscale] = qscale + 8; syntax_mpeg4->c_dc_scaler[qscale] = (qscale+13) >> 1; } if(qscale >= 25 && qscale <= 31) { syntax_mpeg4->y_dc_scaler[qscale] = (qscale << 1) - 16; syntax_mpeg4->c_dc_scaler[qscale] = qscale - 6; } intra_dc_y_scale_table[qscale] = syntax_mpeg4->y_dc_scaler[qscale]; intra_dc_c_scale_table[qscale] = syntax_mpeg4->c_dc_scaler[qscale]; } /* Initialize symbols for shape */ syntax_mpeg4->symbol = (int *) fame_malloc(2048*sizeof(int)); mpeg4_init_symbol(syntax_mpeg4->symbol); /* MPEG-4 uses global mismatch control */ *mismatch_type = fame_mismatch_global; } static void mpeg4_close(fame_syntax_t *syntax) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); int i; syntax_mpeg4->intra_table -= 64*255; /* uncenter intra vlc table */ syntax_mpeg4->inter_table -= 64*255; /* uncenter inter vlc table */ fame_free(syntax_mpeg4->symbol); fame_free(syntax_mpeg4->intra_table); fame_free(syntax_mpeg4->inter_table); for(i = 0; i < syntax_mpeg4->mb_width; i++) { fame_free(syntax_mpeg4->y_pred_v[0][i]); fame_free(syntax_mpeg4->y_pred_v[1][i]); fame_free(syntax_mpeg4->cr_pred_v[i]); fame_free(syntax_mpeg4->cb_pred_v[i]); } fame_free(syntax_mpeg4->y_pred_v[0]); fame_free(syntax_mpeg4->y_pred_v[1]); fame_free(syntax_mpeg4->cr_pred_v); fame_free(syntax_mpeg4->cb_pred_v); fame_free(syntax_mpeg4->y_pred_h[0]); fame_free(syntax_mpeg4->y_pred_h[1]); fame_free(syntax_mpeg4->y_pred_h[2]); fame_free(syntax_mpeg4->cr_pred_h[0]); fame_free(syntax_mpeg4->cb_pred_h[0]); fame_free(syntax_mpeg4->cr_pred_h[1]); fame_free(syntax_mpeg4->cb_pred_h[1]); fame_free(syntax_mpeg4->pred[0]); fame_free(syntax_mpeg4->pred[1]); fame_free(syntax_mpeg4->pred[2]); fame_free(syntax_mpeg4->pred[3]); fame_free(syntax_mpeg4->pred[4]); fame_free(syntax_mpeg4->pred[5]); fame_free(syntax_mpeg4->diff[0]); fame_free(syntax_mpeg4->diff[1]); fame_free(syntax_mpeg4->diff[2]); fame_free(syntax_mpeg4->diff[3]); fame_free(syntax_mpeg4->diff[4]); fame_free(syntax_mpeg4->diff[5]); fame_free(syntax_mpeg4->cae_v->buffer); fame_free(syntax_mpeg4->cae_h->buffer); fame_free(syntax_mpeg4->cae_v); fame_free(syntax_mpeg4->cae_h); fame_free(syntax_mpeg4->motion_pred); } static void mpeg4_use(fame_syntax_t *syntax, unsigned char *buffer, int size) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); bitbuffer_init(&syntax_mpeg4->buffer, buffer, size); } static int mpeg4_flush(fame_syntax_t *syntax) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); return(bitbuffer_flush(&syntax_mpeg4->buffer)); } static void mpeg4_start_sequence(fame_syntax_t *syntax, int width, int height, int fps_num, int fps_den, int size, int bitrate) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); fame_bitbuffer_t *buff; buff = &syntax_mpeg4->buffer; syntax_mpeg4->fps_num = fps_num; syntax_mpeg4->fps_den = fps_den; /* Video Object Layer */ syntax_mpeg4->short_video_header = 0; syntax_mpeg4->random_accessible_vol = 0; syntax_mpeg4->video_object_type_indication = MPEG4_Video_Object_Type_SimpleObject; syntax_mpeg4->is_object_layer_identifier=0; syntax_mpeg4->video_object_layer_verid = 1; syntax_mpeg4->video_object_layer_priority = 1; syntax_mpeg4->aspect_ratio_info = 1; /* square */ syntax_mpeg4->par_width = 1; syntax_mpeg4->par_height = 1; syntax_mpeg4->vol_control_parameters = 0; syntax_mpeg4->video_object_layer_shape_extension = 0; syntax_mpeg4->vop_time_increment_resolution=fps_num; syntax_mpeg4->fixed_vop_time_increment=fps_den; syntax_mpeg4->fixed_vop_rate=1; syntax_mpeg4->video_object_layer_width = width; syntax_mpeg4->video_object_layer_height = height; syntax_mpeg4->interlaced=0; syntax_mpeg4->obmc_disable=1; syntax_mpeg4->sprite_enable=0; syntax_mpeg4->sadct_disable=1; syntax_mpeg4->not_8_bit=0; syntax_mpeg4->quant_precision=5; syntax_mpeg4->bits_per_pixel=8; syntax_mpeg4->quant_type=1; /* MPEG-4 */ syntax_mpeg4->quarter_sample=0; syntax_mpeg4->complexity_estimation_disable=1; syntax_mpeg4->resync_marker_disable=0; syntax_mpeg4->data_partitionned=0; syntax_mpeg4->reversible_vlc=0; syntax_mpeg4->newpred_enable=0; syntax_mpeg4->reduced_resolution_vop_enable=0; syntax_mpeg4->scalability=0; syntax_mpeg4->vop_time_increment=0; syntax_mpeg4->vop_reduced_resolution=0; syntax_mpeg4->interlaced=0; #ifndef OPENDIVX_COMPATIBILITY bitbuffer_write(buff,MPEG4_SEQUENCE_START_CODE,32); bitbuffer_write(buff,syntax_mpeg4->profile_and_level_indication,8); bitbuffer_write(buff,MPEG4_VISUAL_OBJ_START_CODE,32); bitbuffer_write(buff,syntax_mpeg4->is_visual_object_identifier,1); if ( syntax_mpeg4->is_visual_object_identifier ) { /* TODO */ } bitbuffer_write(buff,syntax_mpeg4->visual_object_type,4); if ( (syntax_mpeg4->visual_object_type == MPEG4_Visual_Object_Type_VideoID) || (syntax_mpeg4->visual_object_type == MPEG4_Visual_Object_Type_still_textureID)) { bitbuffer_write(buff,syntax_mpeg4->video_signal_type,1); if(syntax_mpeg4->video_signal_type) { /* TODO */ } } /* Next Start Code */ mpeg4_next_start_code(buff); #endif /* if syntax_mpeg4->visual_object_type == MPEG4_Visual_Object_Type_VideoID * on ne fait rien d'autre */ bitbuffer_write(buff,MPEG4_VIDEO_OBJ_START_CODE, 32); bitbuffer_write(buff,MPEG4_VIDEO_OBJ_LAYER_START_CODE, 32); syntax_mpeg4->short_video_header=0; bitbuffer_write(buff,syntax_mpeg4->random_accessible_vol,1); bitbuffer_write(buff,syntax_mpeg4->video_object_type_indication,8); if (syntax_mpeg4->video_object_type_indication == MPEG4_Video_Object_Type_FineGranularityScalable ) { /* Not Supported */ } else { bitbuffer_write(buff,syntax_mpeg4->is_object_layer_identifier, 1 ); if(syntax_mpeg4->is_object_layer_identifier) { /* TODO */ } bitbuffer_write(buff,syntax_mpeg4->aspect_ratio_info,4); if (syntax_mpeg4->aspect_ratio_info == MPEG4_Aspect_Ratio_Info_ExtendedPAR ) { /* TODO */ } bitbuffer_write(buff,syntax_mpeg4->vol_control_parameters, 1); if (syntax_mpeg4->vol_control_parameters) { /* TODO */ } bitbuffer_write(buff,syntax_mpeg4->video_object_layer_shape,2); if ((syntax_mpeg4->video_object_layer_shape == MPEG4_Video_Object_Layer_Shape_Grayscale) && (syntax_mpeg4->video_object_layer_verid != 1)) { /* TODO */ } bitbuffer_write(buff,1,1); /* Marker */ bitbuffer_write(buff,syntax_mpeg4->vop_time_increment_resolution, 16); bitbuffer_write(buff,1,1); /* Marker */ bitbuffer_write(buff,syntax_mpeg4->fixed_vop_rate,1); if(syntax_mpeg4->fixed_vop_rate); { bitbuffer_write(buff,syntax_mpeg4->fixed_vop_time_increment, get_min_bit(syntax_mpeg4->vop_time_increment_resolution)); } if(syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_BinaryOnly ) { if(syntax_mpeg4->video_object_layer_shape == MPEG4_Video_Object_Layer_Shape_Rectangular ) { bitbuffer_write(buff,1,1); /* Marker */ bitbuffer_write(buff,syntax_mpeg4->video_object_layer_width,13); bitbuffer_write(buff,1,1); /* Marker */ bitbuffer_write(buff,syntax_mpeg4->video_object_layer_height,13); bitbuffer_write(buff,1,1); /* Marker */ } bitbuffer_write(buff,syntax_mpeg4->interlaced,1); bitbuffer_write(buff,syntax_mpeg4->obmc_disable,1); if (syntax_mpeg4->video_object_layer_verid == 0x01 ) { bitbuffer_write(buff,syntax_mpeg4->sprite_enable,1); } else bitbuffer_write(buff,syntax_mpeg4->sprite_enable,2); /* TODO test sprite */ if ((syntax_mpeg4->video_object_layer_verid != 0x01) && (syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_Rectangular )) bitbuffer_write(buff,syntax_mpeg4->sadct_disable,1); bitbuffer_write(buff,syntax_mpeg4->not_8_bit,1); if (syntax_mpeg4->not_8_bit) { bitbuffer_write(buff,syntax_mpeg4->quant_precision,4); bitbuffer_write(buff,syntax_mpeg4->bits_per_pixel,4); } if ( syntax_mpeg4->video_object_layer_shape == MPEG4_Video_Object_Layer_Shape_Grayscale ) { /* not supported */ } bitbuffer_write(buff,syntax_mpeg4->quant_type,1); if ( syntax_mpeg4->quant_type) { int i; bitbuffer_write(buff,syntax_mpeg4->load_intra_quant_mat,1); if ( syntax_mpeg4->load_intra_quant_mat ) for(i = 0; i < 64; i++) /* already in zigzag order */ bitbuffer_write(buff,syntax_mpeg4->intra_quant_mat[i],8); bitbuffer_write(buff,syntax_mpeg4->load_nonintra_quant_mat,1); if ( syntax_mpeg4->load_nonintra_quant_mat ) for(i = 0; i < 64; i++) /* already in zigzag order */ bitbuffer_write(buff,syntax_mpeg4->nonintra_quant_mat[i],8); if( syntax_mpeg4->video_object_layer_shape == MPEG4_Video_Object_Layer_Shape_Grayscale ) { /* not supported */ } } if(syntax_mpeg4->video_object_layer_verid != 0x01) bitbuffer_write(buff,syntax_mpeg4->quarter_sample,1); bitbuffer_write(buff,syntax_mpeg4->complexity_estimation_disable,1); if(!syntax_mpeg4->complexity_estimation_disable) { /* TODO */ } bitbuffer_write(buff,syntax_mpeg4->resync_marker_disable,1); bitbuffer_write(buff,syntax_mpeg4->data_partitionned,1); if(syntax_mpeg4->data_partitionned) bitbuffer_write(buff,syntax_mpeg4->reversible_vlc,1); if (syntax_mpeg4->video_object_layer_verid != 0x01 ) { bitbuffer_write(buff,syntax_mpeg4->newpred_enable,1); if (syntax_mpeg4->newpred_enable) { /* TODO */ } bitbuffer_write(buff,syntax_mpeg4->reduced_resolution_vop_enable,1); } bitbuffer_write(buff,syntax_mpeg4->scalability,1); if (syntax_mpeg4->scalability) { /* TODO */ } } else { /* Binary Only not (yet) supported */ } /* Next Start Code */ mpeg4_next_start_code(buff); /* User Data */ } } static void mpeg4_start_GOP(fame_syntax_t *syntax, int frame) { #ifndef OPENDIVX_COMPATIBILITY fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); fame_bitbuffer_t *buff; int fps_num, fps_den; fps_num = syntax_mpeg4->fps_num; fps_den = syntax_mpeg4->fps_den; buff = &syntax_mpeg4->buffer; /* Group Of VOP default values */ syntax_mpeg4->closed_gov=1; syntax_mpeg4->broken_link=0; syntax_mpeg4->vop_time_increment=0; bitbuffer_write(buff,MPEG4_GVOP_START_CODE, 32); /* timecount hours */ bitbuffer_write(buff, (frame*fps_den/(3600*fps_num)) & 0x1f, 5); /* timecount minutes */ bitbuffer_write(buff, ((frame*fps_den/(60*fps_num))%60) & 0x3f, 6); /* marker */ bitbuffer_write(buff, 1, 1); /* timecount seconds */ bitbuffer_write(buff, ((frame*fps_den/fps_num)%60) & 0x3f, 6); bitbuffer_write(buff, syntax_mpeg4->closed_gov, 1); bitbuffer_write(buff, syntax_mpeg4->broken_link, 1); /* Next Start Code */ mpeg4_next_start_code(buff); #endif } static void mpeg4_start_picture(fame_syntax_t *syntax, char frame_type, int frame_number, fame_box_t *box, int rounding_control, int search_range) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); fame_bitbuffer_t *buff; int tmp; unsigned int modulo_time_base; buff = &syntax_mpeg4->buffer; switch(frame_type) { case 'I': syntax_mpeg4->vop_coding_type = MPEG4_I_FRAME; break; case 'P': syntax_mpeg4->vop_coding_type = MPEG4_P_FRAME; break; default: FAME_ERROR("Unsupported picture coding type %c", frame_type); return; } /* Video Object Plane */ syntax_mpeg4->vop_rounding_type = rounding_control; syntax_mpeg4->vop_reduced_resolution = 0; syntax_mpeg4->vop_horizontal_mc_spatial_ref=box->x; syntax_mpeg4->vop_vertical_mc_spatial_ref=box->y; syntax_mpeg4->vop_width=box->w; syntax_mpeg4->vop_height=box->h; syntax_mpeg4->vop_constant_alpha=0; syntax_mpeg4->vop_constant_alpha_value=128; syntax_mpeg4->intra_dc_vlc_thr=0; /* use intra DC vlc for all DC coefficients */ tmp = (box->w+15)/16 * (box->h+15)/16; syntax_mpeg4->macroblock_number_size = get_min_bit(tmp); /* compute fcode */ syntax_mpeg4->vop_fcode_forward = syntax_mpeg4->vop_fcode_backward = fame_max(1, get_min_bit(search_range-1)-3); if(syntax_mpeg4->vop_fcode_forward > 7) { FAME_WARNING("vop_fcode_forward > 7, search range too big.\n"); syntax_mpeg4->vop_fcode_forward = 7; } if(syntax_mpeg4->vop_fcode_backward > 7) { FAME_WARNING("vop_fcode_backward > 7, search range too big.\n"); syntax_mpeg4->vop_fcode_backward = 7; } bitbuffer_write(buff, MPEG4_VOP_START_CODE, 32); /* picture start code */ bitbuffer_write(buff, syntax_mpeg4->vop_coding_type,2); /* Modulo time base */ modulo_time_base = syntax_mpeg4->vop_time_increment / syntax_mpeg4->vop_time_increment_resolution; if (modulo_time_base) { bitbuffer_write(buff, ((1 << modulo_time_base) - 1), modulo_time_base); syntax_mpeg4->vop_time_increment %= syntax_mpeg4->vop_time_increment_resolution; } bitbuffer_write(buff,0,1); /* end of modulo time base */ bitbuffer_write(buff,1,1); /* Marker */ bitbuffer_write(buff,syntax_mpeg4->vop_time_increment%syntax_mpeg4->vop_time_increment_resolution, get_min_bit(syntax_mpeg4->vop_time_increment_resolution)); syntax_mpeg4->vop_time_increment += syntax_mpeg4->fixed_vop_time_increment; bitbuffer_write(buff,1,1); bitbuffer_write(buff,1,1); /* VOP coded */ if(syntax_mpeg4->newpred_enable) { /* not supported */ } if( (syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_BinaryOnly ) && (syntax_mpeg4->vop_coding_type == MPEG4_P_FRAME)) bitbuffer_write(buff,syntax_mpeg4->vop_rounding_type,1); if ( (syntax_mpeg4->reduced_resolution_vop_enable) && (syntax_mpeg4->video_object_layer_shape == MPEG4_Video_Object_Layer_Shape_Rectangular ) && ((syntax_mpeg4->vop_coding_type == MPEG4_I_FRAME) || (syntax_mpeg4->vop_coding_type == MPEG4_P_FRAME ))) bitbuffer_write(buff,syntax_mpeg4->vop_reduced_resolution,1); if(syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_Rectangular ) { /* TODO: test Sprite */ { bitbuffer_write(buff,syntax_mpeg4->vop_width & 0x1fff,13); bitbuffer_write(buff,1,1); /* Marker */ bitbuffer_write(buff,syntax_mpeg4->vop_height & 0x1fff,13); bitbuffer_write(buff,1,1); /* Marker */ bitbuffer_write(buff,syntax_mpeg4->vop_horizontal_mc_spatial_ref & 0x1fff,13); bitbuffer_write(buff,1,1); /* Marker */ bitbuffer_write(buff,syntax_mpeg4->vop_vertical_mc_spatial_ref & 0x1fff,13); bitbuffer_write(buff,1,1); /* Marker */ } bitbuffer_write(buff,syntax_mpeg4->change_conv_ratio_disable,1); bitbuffer_write(buff,syntax_mpeg4->vop_constant_alpha,1); if(syntax_mpeg4->vop_constant_alpha) bitbuffer_write(buff,syntax_mpeg4->vop_constant_alpha_value,8) } if (syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_BinaryOnly ) if (!syntax_mpeg4->complexity_estimation_disable) { /* not supported */ } if (syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_BinaryOnly ) { bitbuffer_write(buff,syntax_mpeg4->intra_dc_vlc_thr,3); if(syntax_mpeg4->interlaced) { /* not supported */ } } /* TODO: sprites */ { } syntax_mpeg4->flag_video_packet_header = 0; /* restart motion predictor */ syntax_mpeg4->mv_pred = syntax_mpeg4->motion_pred; } static void inline mpeg4_reset_pred(fame_syntax_t *syntax) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); int i; /* horizontal predictors */ FASTCOPY16(syntax_mpeg4->y_pred_h[0], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_h[2], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_h[0], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[0], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[1], syntax_mpeg4->pred_default); /* vertical predictors */ for(i = 0; i < syntax_mpeg4->mb_width; i++) { FASTCOPY16(syntax_mpeg4->y_pred_v[0][i], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_v[1][i], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_v[i], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_v[i], syntax_mpeg4->pred_default); } /* motion predictors */ memset(syntax_mpeg4->motion_pred, 0, 4*syntax_mpeg4->mb_height* syntax_mpeg4->mb_width*sizeof(fame_motion_vector_t)); } static void mpeg4_start_slice(fame_syntax_t *syntax, int vpos, int length, unsigned char qscale) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); fame_bitbuffer_t *buff; /* compute dc_scaler */ if(qscale == 0 || qscale > 31) { FAME_WARNING("Invalid quantisation scale %d (1-31), setting to 8.\n", qscale); } buff = &syntax_mpeg4->buffer; /* Video Packet default value */ syntax_mpeg4->vop_quant = syntax_mpeg4->quant_scale = qscale; syntax_mpeg4->vop_shape_coding_type = 0; /* always intra shape coding */ syntax_mpeg4->header_extension_code = 0; syntax_mpeg4->macroblock_number = vpos * ((syntax_mpeg4->vop_width+15) / 16); if (!syntax_mpeg4->flag_video_packet_header) { if (syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_BinaryOnly ) { bitbuffer_write(buff,syntax_mpeg4->vop_quant & ((1 << syntax_mpeg4->quant_precision)-1),syntax_mpeg4->quant_precision); if (syntax_mpeg4->video_object_layer_shape == MPEG4_Video_Object_Layer_Shape_Grayscale ) { /* Not supported */ } if (syntax_mpeg4->vop_coding_type != MPEG4_I_FRAME ) bitbuffer_write(buff,syntax_mpeg4->vop_fcode_forward & 7,3); if (syntax_mpeg4->vop_coding_type == MPEG4_B_FRAME ) bitbuffer_write(buff,syntax_mpeg4->vop_fcode_backward & 7,3); if(!syntax_mpeg4->scalability) { if ((syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_Rectangular) && (syntax_mpeg4->vop_coding_type != MPEG4_I_FRAME )) bitbuffer_write(buff,syntax_mpeg4->vop_shape_coding_type,1); }else { /* Not supported */ } } else { /* Not supported */ } syntax_mpeg4->flag_video_packet_header=1; } else { int resync_length = 0; /* compute resync marker length */ if (syntax_mpeg4->vop_coding_type == MPEG4_I_FRAME || syntax_mpeg4->video_object_layer_shape == MPEG4_Video_Object_Layer_Shape_BinaryOnly) resync_length = 16; else { if(syntax_mpeg4->vop_coding_type == MPEG4_P_FRAME) resync_length = 15+syntax_mpeg4->vop_fcode_forward; if(syntax_mpeg4->vop_coding_type == MPEG4_B_FRAME) resync_length = fame_max(15+fame_max(syntax_mpeg4->vop_fcode_forward, syntax_mpeg4->vop_fcode_backward), 17); } /* resync marker */ bitbuffer_write(buff, 0, resync_length); bitbuffer_write(buff, 1, 1); if(syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_Rectangular) bitbuffer_write(buff,syntax_mpeg4->header_extension_code,1); if(syntax_mpeg4->header_extension_code) /* TODO: test sprite_enable */ { /* Not implemented */ } bitbuffer_write(buff,syntax_mpeg4->macroblock_number,syntax_mpeg4->macroblock_number_size); if(syntax_mpeg4->video_object_layer_shape!= MPEG4_Video_Object_Layer_Shape_BinaryOnly) bitbuffer_write(buff,syntax_mpeg4->quant_scale & 0x1f, 5); if(syntax_mpeg4->video_object_layer_shape == MPEG4_Video_Object_Layer_Shape_Rectangular) bitbuffer_write(buff,syntax_mpeg4->header_extension_code,1); if(syntax_mpeg4->header_extension_code) { /* Not Implemented */ } if (syntax_mpeg4->newpred_enable) { /* Not implemented */ } } /* reset the DC predictors to their original values */ mpeg4_reset_pred(syntax); } static void mpeg4_end_slice(fame_syntax_t *syntax) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); mpeg4_next_start_code(&syntax_mpeg4->buffer); /* next start code */ } static void mpeg4_end_sequence(fame_syntax_t *syntax) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); /* end sequence code */ bitbuffer_write(&syntax_mpeg4->buffer, MPEG4_SEQUENCE_END_CODE, 32); } static void mpeg4_predict_vector(fame_syntax_t *syntax, int mb_x, int mb_y, int k, fame_motion_vector_t *mv) { #define MEDIAN(a,b,c) ((b)<(a))?(((c)>(a))?(a):(((c)<(b))?(b):(c))):(((c)<(a))?(a):(((c)>(b))?(b):(c))) fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); fame_motion_vector_t *predictor1, *predictor2, *predictor3, *mv_pred; int pitch; int border1, border2, border3; int mb_addr, slice_addr; /* HACK: we use count as a flag for vector validity */ /* TODO: thus, checking borders is probably not needed anymore */ /* though checking video packets boundaries is. */ pitch = syntax_mpeg4->mb_width; slice_addr = syntax_mpeg4->macroblock_number; mb_addr = mb_y*syntax_mpeg4->mb_width+mb_x; border1 = (mb_x == 0) || (mb_addr <= slice_addr); border2 = (mb_addr - pitch < slice_addr); border3 = (mb_x == pitch - 1) || (mb_addr - pitch + 1 < slice_addr); pitch = syntax_mpeg4->mb_width*4; mv_pred = syntax_mpeg4->mv_pred; switch(k) { case 0: predictor1 = mv_pred - 3; predictor2 = mv_pred - pitch + 2; predictor3 = mv_pred - pitch + 6; break; /* WARNING: count must be valid in 'mv' past this point */ case 1: predictor1 = &mv[0]; border1 = 0; predictor2 = mv_pred - pitch + 3; predictor3 = mv_pred - pitch + 6; break; case 2: predictor1 = mv_pred - 1; predictor2 = &mv[0]; border2 = 0; predictor3 = &mv[1]; border3 = 0; break; case 3: predictor1 = &mv[2]; border1 = 0; predictor2 = &mv[0]; border2 = 0; predictor3 = &mv[1]; border3 = 0; break; default: /* invalid k */ predictor1 = predictor2 = predictor3 = NULL; break; } if(border1 || !predictor1->count) { if(border2 || !predictor2->count) { if(border3 || !predictor3->count) { /* no pred */ mv_pred[k].dx = 0; mv_pred[k].dy = 0; } else { /* only p3 */ mv_pred[k].dx = predictor3->dx; mv_pred[k].dy = predictor3->dy; } } else { if(border3 || !predictor3->count) { /* p2 only */ mv_pred[k].dx = predictor2->dx; mv_pred[k].dy = predictor2->dx; } else { /* p2, p3 */ mv_pred[k].dx = MEDIAN(0,predictor2->dx,predictor3->dx); mv_pred[k].dy = MEDIAN(0,predictor2->dy,predictor3->dy); } } } else if(border3 || !predictor3->count) { if(border2 || !predictor2->count) { /* p1 only */ mv_pred[k].dx = predictor1->dx; mv_pred[k].dy = predictor1->dy; } else { /* p1, p2 */ mv_pred[k].dx = MEDIAN(predictor1->dx,predictor2->dx,0); mv_pred[k].dy = MEDIAN(predictor1->dy,predictor2->dy,0); } } else { if(border2 || !predictor2->count) { /* p1, p3 */ mv_pred[k].dx = MEDIAN(predictor1->dx,0,predictor3->dx); mv_pred[k].dy = MEDIAN(predictor1->dx,0,predictor3->dx); } else { /* p1, p2, p3 */ mv_pred[k].dx = MEDIAN(predictor1->dx, predictor2->dx, predictor3->dx); mv_pred[k].dy = MEDIAN(predictor1->dy, predictor2->dy, predictor3->dy); } } if(k == 0) { /* HACK: fill in the 16x16 vector with the predictor */ mv[0].dx = mv_pred[0].dx; mv[0].dy = mv_pred[0].dy; } } static void inline mpeg4_arithmetic_bit(fame_syntax_cae_t *cae, unsigned char bit) { *cae->sequence++ = bit; /* avoid start code emulation */ if (bit == 0) { cae->nzeros--; if (cae->nzeros == 0) { *cae->sequence++ = 1; cae->nonzero = 1; cae->nzeros = CAE_MAX_MIDDLE; } } else { cae->nonzero = 1; cae->nzeros = CAE_MAX_MIDDLE; } } static void inline mpeg4_arithmetic_follow(fame_syntax_cae_t *cae, unsigned char bit) { if (!cae->first_bit) mpeg4_arithmetic_bit(cae, bit); else cae->first_bit = 0; while(cae->bits_to_follow > 0) { mpeg4_arithmetic_bit(cae, !bit); cae->bits_to_follow--; } } static void inline mpeg4_arithmetic_renormalize(fame_syntax_cae_t *cae) { while (cae->range < CAE_1_4) { if (cae->lower >= CAE_1_2) { mpeg4_arithmetic_follow(cae, 1); cae->lower -= CAE_1_2; } else { if (cae->lower + cae->range <= CAE_1_2) mpeg4_arithmetic_follow(cae, 0); else { cae->bits_to_follow++; cae->lower -= CAE_1_4; } } cae->lower += cae->lower; cae->range += cae->range; } } static void inline mpeg4_arithmetic_enter(fame_syntax_cae_t *cae) { cae->lower = 0; cae->range = CAE_1_2 - 1; cae->bits_to_follow = 0; cae->first_bit = 1; cae->nzeros = CAE_MAX_HEADING; cae->nonzero = 0; cae->sequence = cae->buffer; } static void inline mpeg4_arithmetic_leave(fame_syntax_cae_t *cae) { int a = (cae->lower) >> 29; int b = (cae->range + cae->lower) >> 29; int nbits, bits, i; if (b == 0) b = 8; if (b - a >= 4 || (b - a == 3 && (a & 1))) { nbits = 2; bits = (a >> 1) + 1; } else { nbits = 3; bits = a + 1; } for (i = 1; i <= nbits; i++) mpeg4_arithmetic_follow(cae, (bits >> (nbits - i)) & 1); if (cae->nzeros < CAE_MAX_MIDDLE - CAE_MAX_TRAILING || cae->nonzero == 0) { mpeg4_arithmetic_follow(cae, 1); } *cae->sequence++ = 0xff; } static void inline mpeg4_arithmetic_code(fame_syntax_cae_t *cae, int cLPS) { signed long rLPS; rLPS = (cae->range >> 16) * cLPS; if (rLPS > 0) { cae->lower += cae->range - rLPS; cae->range = rLPS; } else cae->range += rLPS; mpeg4_arithmetic_renormalize(cae); } static void inline mpeg4_write_intra_bab(fame_syntax_t *syntax, int mb_x, int mb_y, unsigned char *bab, unsigned char *prev_bab, fame_bab_t bab_type) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); int i, j; int line; unsigned char *ptr_h, *ptr_v; int pitch = syntax_mpeg4->mb_width + 2; int addr; int prev_addr[4]; /* initialize arithmetic encoders */ mpeg4_arithmetic_enter(syntax_mpeg4->cae_h); mpeg4_arithmetic_enter(syntax_mpeg4->cae_v); /* compute current bab address, add 1 for borders */ addr = mb_y * pitch + pitch + mb_x + 1; /* compute addresses of previous babs */ prev_addr[0] = addr - 1; prev_addr[1] = addr - pitch + 1; prev_addr[2] = addr - pitch; prev_addr[3] = addr - pitch - 1; /* compute context number for vlc table indexation */ /* bab = 0 if not_coded */ /* bab = 1 if all_coded */ /* bab = 2 if cae_coded */ /* context = 27*bab[i-1,j-1]+9*bab[i,j-1]+3*bab[i+1,j-1]+bab[i-1,j] */ /* TODO: fix context computation in case of multiple slice */ /* TODO: wrong for P frames */ i = 0; for(j = 0; j < 4; j++) { switch(prev_bab[prev_addr[j]]) { case bab_not_coded: break; case bab_all_coded: i += bab_type_intra_context_weight[j]; break; case bab_border_16x16: case bab_border_8x8: case bab_border_4x4: i += bab_type_intra_context_weight[j]; i += bab_type_intra_context_weight[j]; break; } } /* save current bab_type for further context computing */ prev_bab[addr] = bab_type; /* write vlc */ switch(bab_type) { case bab_not_coded: bitbuffer_write(&syntax_mpeg4->buffer, 1, bab_type_intra_vl[i][0]); return; case bab_all_coded: bitbuffer_write(&syntax_mpeg4->buffer, 1, bab_type_intra_vl[i][1]); return; case bab_border_16x16: case bab_border_8x8: case bab_border_4x4: bitbuffer_write(&syntax_mpeg4->buffer, 1, bab_type_intra_vl[i][2]); break; } /* no MVDs */ /* horizontal raster order context */ #define CIH0 ((*(ptr_h ))<<0) #define CIH1 ((*(ptr_h -1))<<1) #define CIH2 ((*(ptr_h -2))<<2) #define CIH3 ((*(ptr_h-1*line+2))<<3) #define CIH4 ((*(ptr_h-1*line+1))<<4) #define CIH5 ((*(ptr_h-1*line ))<<5) #define CIH6 ((*(ptr_h-1*line-1))<<6) #define CIH7 ((*(ptr_h-1*line-2))<<7) #define CIH8 ((*(ptr_h-2*line+1))<<8) #define CIH9 ((*(ptr_h-2*line ))<<9) #define CIH10 ((*(ptr_h-2*line-1))<<10) #define CIH (CIH0|CIH1|CIH2|CIH3|CIH4|CIH5|CIH6|CIH7|CIH8|CIH9|CIH10) /* vertical raster order context */ #define CIV0 ((*(ptr_v ))<<0) #define CIV1 ((*(ptr_v -1*line))<<1) #define CIV2 ((*(ptr_v -2*line))<<2) #define CIV3 ((*(ptr_v-1+2*line))<<3) #define CIV4 ((*(ptr_v-1+1*line))<<4) #define CIV5 ((*(ptr_v-1 ))<<5) #define CIV6 ((*(ptr_v-1-1*line))<<6) #define CIV7 ((*(ptr_v-1-2*line))<<7) #define CIV8 ((*(ptr_v-2+1*line))<<8) #define CIV9 ((*(ptr_v-2 ))<<9) #define CIV10 ((*(ptr_v-2-1*line))<<10) #define CIV (CIV0|CIV1|CIV2|CIV3|CIV4|CIV5|CIV6|CIV7|CIV8|CIV9|CIV10) switch(bab_type) { case bab_border_16x16: if(!syntax_mpeg4->change_conv_ratio_disable) bitbuffer_write(&syntax_mpeg4->buffer, 0, 1); /* CR = 1 */ line = 20; ptr_h = ptr_v = bab+2*line+2; /* skip border */ for(i = 0; i < 16; i++) { for(j = 0; j < 16; j++) { mpeg4_arithmetic_code(syntax_mpeg4->cae_h, syntax_mpeg4->symbol[CIH]); mpeg4_arithmetic_code(syntax_mpeg4->cae_v, syntax_mpeg4->symbol[CIV]); ptr_h++; ptr_v+=line; } ptr_h += 4; ptr_v += -(line<<4) + 1; } break; case bab_border_8x8: bitbuffer_write(&syntax_mpeg4->buffer, 2, 2); /* CR = 2 */ line = 12; ptr_h = ptr_v = bab+2*line+2; /* skip border */ for(i = 0; i < 8; i++) { for(j = 0; j < 8; j++) { mpeg4_arithmetic_code(syntax_mpeg4->cae_h, syntax_mpeg4->symbol[CIH]); mpeg4_arithmetic_code(syntax_mpeg4->cae_v, syntax_mpeg4->symbol[CIV]); ptr_h++; ptr_v+=line; } ptr_h += 4; ptr_v += -(line<<3) + 1; } break; case bab_border_4x4: bitbuffer_write(&syntax_mpeg4->buffer, 3, 2); /* CR = 4 */ line = 8; ptr_h = ptr_v = bab+2*line+2; /* skip border */ for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { mpeg4_arithmetic_code(syntax_mpeg4->cae_h, syntax_mpeg4->symbol[CIH]); mpeg4_arithmetic_code(syntax_mpeg4->cae_v, syntax_mpeg4->symbol[CIV]); ptr_h++; ptr_v+=line; } ptr_h += 4; ptr_v += -(line<<2) + 1; } break; default: break; } mpeg4_arithmetic_leave(syntax_mpeg4->cae_h); mpeg4_arithmetic_leave(syntax_mpeg4->cae_v); /* keep the smallest code */ syntax_mpeg4->cae_h->sequence = syntax_mpeg4->cae_h->buffer; syntax_mpeg4->cae_v->sequence = syntax_mpeg4->cae_v->buffer; for(i = 0; syntax_mpeg4->cae_h->sequence[i] != 0xff; i++); for(j = 0; syntax_mpeg4->cae_v->sequence[j] != 0xff; j++); if(i <= j) { /* horizontal */ bitbuffer_write(&syntax_mpeg4->buffer, 1, 1); /* don't transpose */ for(j = 0; j < i; j++) { bitbuffer_write(&syntax_mpeg4->buffer, syntax_mpeg4->cae_h->sequence[j], 1); } } else { /* vertical */ bitbuffer_write(&syntax_mpeg4->buffer, 0, 1); /* transpose */ for(i = 0; i < j; i++) { bitbuffer_write(&syntax_mpeg4->buffer, syntax_mpeg4->cae_v->sequence[i], 1); } } } static void mpeg4_block_intra(fame_syntax_t *syntax, short *block, fame_vlc_t const *table, short v, unsigned char *zigzag, unsigned char coded) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); short i; unsigned long last; fame_vlc_t const *vlc; fame_bitbuffer_t * const buffer = &syntax_mpeg4->buffer; unsigned char * data = buffer->data; unsigned long shift = buffer->shift; /* encode DC coefficient */ fast_bitbuffer_write(data, shift, table[v+255].code, table[v+255].length); /* encode AC coefficients */ if(coded) { i = 1; last = 1; /* i < 64 checking not needed as all-zero block is not coded */ while((v = block[zigzag[i]]) == 0) i++; do { /* count zeroes */ vlc = syntax_mpeg4->intra_table + (mpeg4_table_clip[v] << 6) + i - last; last = ++i; while(i < 64 && (v = block[zigzag[i]]) == 0) i++; /* write code */ if(i != 64) { fast_bitbuffer_write(data, shift, vlc->code, vlc->length); } else { vlc += 64*511; fast_bitbuffer_write(data, shift, vlc->code, vlc->length); break; } } while(1); } buffer->data = data; buffer->shift = shift; } static int mpeg4_write_intra_mb(fame_syntax_t *syntax, int mb_x, int mb_y, short *blocks[6], unsigned char *bab, unsigned char *bab_map, fame_bab_t bab_type, int dquant, unsigned char pattern) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); int coded[6]; int i, j; int cbp, bc; short *a, *b, *c; short *p[6]; short ys, cs, qs; short *d[6]; short o[6]; short ac_sad; unsigned char *zigzag[6]; int retval; p[0] = syntax_mpeg4->pred[0]; p[1] = syntax_mpeg4->pred[1]; p[2] = syntax_mpeg4->pred[2]; p[3] = syntax_mpeg4->pred[3]; p[4] = syntax_mpeg4->pred[4]; p[5] = syntax_mpeg4->pred[5]; d[0] = syntax_mpeg4->diff[0]; d[1] = syntax_mpeg4->diff[1]; d[2] = syntax_mpeg4->diff[2]; d[3] = syntax_mpeg4->diff[3]; d[4] = syntax_mpeg4->diff[4]; d[5] = syntax_mpeg4->diff[5]; o[0] = o[1] = o[3] = o[4] = o[4] = o[5] = TOP_PREDICTED; FASTCOPY16(p[0], syntax_mpeg4->pred_default); FASTCOPY16(p[2], syntax_mpeg4->pred_default); FASTCOPY16(p[1], syntax_mpeg4->pred_default); FASTCOPY16(p[3], syntax_mpeg4->pred_default); FASTCOPY16(p[4], syntax_mpeg4->pred_default); FASTCOPY16(p[5], syntax_mpeg4->pred_default); if(syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_Rectangular) mpeg4_write_intra_bab(syntax, mb_x, mb_y, bab, bab_map, bab_type); if(bab_type != bab_not_coded) { /* not transparent */ if(syntax_mpeg4->vop_coding_type != MPEG4_I_FRAME) bitbuffer_write(&syntax_mpeg4->buffer, 0, 1); syntax_mpeg4->quant_scale += dquant; qs = syntax_mpeg4->quant_scale; ys = syntax_mpeg4->y_dc_scaler[qs]; cs = syntax_mpeg4->c_dc_scaler[qs]; /* prediction */ if(!mb_x) { /* start of line, reset horizontal predictors */ FASTCOPY16(syntax_mpeg4->y_pred_h[0], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_h[2], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[0], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_h[0], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_h[1], syntax_mpeg4->pred_default); } ac_sad = 0; /* Y (0,0) block */ if(pattern & 1) { a = syntax_mpeg4->y_pred_h[1]; /* left block */ b = syntax_mpeg4->y_pred_h[0]; /* top left block */ c = syntax_mpeg4->y_pred_v[0][mb_x]; /* top block */ /* choose between vertical and horizontal prediction */ if(abs(*a - *b) < abs(*b - *c)) { FASTAC8H(d[0], blocks[0]); FASTSCALE8H(p[0], c, qs); FASTSAD8H(ac_sad, p[0], d[0]); o[0] = TOP_PREDICTED; *p[0] = (*p[0] + (ys>>1)) / ys; FASTDIFF8H(d[0], p[0]); *d[0] = mpeg4_table_clip[*d[0]]; FASTSUM8H(p[0], d[0]); *p[0] *= ys; } else { FASTAC8V(d[0], blocks[0]); FASTSCALE8V(p[0], a, qs); FASTSAD8V(ac_sad, p[0], d[0]); o[0] = LEFT_PREDICTED; *p[0] = (*p[0] + (ys>>1)) / ys; FASTDIFF8V(d[0], p[0]); *d[0] = mpeg4_table_clip[*d[0]]; FASTSUM8V(p[0], d[0]); *p[0] *= ys; } p[0][FASTQS] = qs; } /* Y (0,1) block */ if(pattern & 2) { a = p[0]; /* left block */ b = syntax_mpeg4->y_pred_v[0][mb_x]; /* top left block */ c = syntax_mpeg4->y_pred_v[1][mb_x]; /* top block */ /* choose between vertical and horizontal prediction */ if(abs(*a - *b) < abs(*b - *c)) { FASTAC8H(d[1], blocks[1]); FASTSCALE8H(p[1], c, qs); FASTSAD8H(ac_sad, p[1], d[1]); o[1] = TOP_PREDICTED; *p[1] = (*p[1] + (ys>>1)) / ys; FASTDIFF8H(d[1], p[1]); *d[1] = mpeg4_table_clip[*d[1]]; FASTSUM8H(p[1], d[1]); *p[1] *= ys; } else { FASTAC8V(d[1], blocks[1]); FASTSCALE8V(p[1], a, qs); FASTSAD8V(ac_sad, p[1], d[1]); o[1] = LEFT_PREDICTED; *p[1] = (*p[1] + (ys>>1)) / ys; FASTDIFF8V(d[1], p[1]); *d[1] = mpeg4_table_clip[*d[1]]; FASTSUM8V(p[1], d[1]); *p[1] *= ys; } p[1][FASTQS] = qs; } /* Y (1,0) block */ if(pattern & 4) { a = syntax_mpeg4->y_pred_h[2]; /* left block */ b = syntax_mpeg4->y_pred_h[1]; /* top left block */ c = p[0]; /* top block */ /* choose between vertical and horizontal prediction */ if(abs(*a - *b) < abs(*b - *c)) { FASTAC8H(d[2], blocks[2]); FASTSCALE8H(p[2], c, qs); FASTSAD8H(ac_sad, p[2], d[2]); o[2] = TOP_PREDICTED; *p[2] = (*p[2] + (ys>>1)) / ys; FASTDIFF8H(d[2], p[2]); *d[2] = mpeg4_table_clip[*d[2]]; FASTSUM8H(p[2], d[2]); *p[2] *= ys; } else { FASTAC8V(d[2], blocks[2]); FASTSCALE8V(p[2], a, qs); FASTSAD8V(ac_sad, p[2], d[2]); o[2] = LEFT_PREDICTED; *p[2] = (*p[2] + (ys>>1)) / ys; FASTDIFF8V(d[2], p[2]); *d[2] = mpeg4_table_clip[*d[2]]; FASTSUM8V(p[2], d[2]); *p[2] *= ys; } p[2][FASTQS] = qs; } /* Y (1,1) block */ if(pattern & 8) { a = p[2]; /* left block */ b = p[0]; /* top left block */ c = p[1]; /* top block */ /* choose between vertical and horizontal prediction */ if(abs(*a - *b) < abs(*b - *c)) { FASTAC8H(d[3], blocks[3]); FASTSCALE8H(p[3], c, qs); FASTSAD8H(ac_sad, p[3], d[3]); o[3] = TOP_PREDICTED; *p[3] = (*p[3] + (ys>>1)) / ys; FASTDIFF8H(d[3], p[3]); *d[3] = mpeg4_table_clip[*d[3]]; FASTSUM8H(p[3], d[3]); *p[3] *= ys; } else { FASTAC8V(d[3], blocks[3]); FASTSCALE8V(p[3], a, qs); FASTSAD8V(ac_sad, p[3], d[3]); o[3] = LEFT_PREDICTED; *p[3] = (*p[3] + (ys>>1)) / ys; FASTDIFF8V(d[3], p[3]); *d[3] = mpeg4_table_clip[*d[3]]; FASTSUM8V(p[3], d[3]); *p[3] *= ys; } p[3][FASTQS] = qs; } /* Cb block */ a = syntax_mpeg4->cb_pred_h[1]; /* left block */ b = syntax_mpeg4->cb_pred_h[0]; /* top left block */ c = syntax_mpeg4->cb_pred_v[mb_x]; /* top block */ /* choose between vertical and horizontal prediction */ if(abs(*a - *b) < abs(*b - *c)) { FASTAC8H(d[4], blocks[4]); FASTSCALE8H(p[4], c, qs); FASTSAD8H(ac_sad, p[4], d[4]); o[4] = TOP_PREDICTED; *p[4] = (*p[4] + (cs>>1)) / cs; FASTDIFF8H(d[4], p[4]); *d[4] = mpeg4_table_clip[*d[4]]; FASTSUM8H(p[4], d[4]); *p[4] *= cs; } else { FASTAC8V(d[4], blocks[4]); FASTSCALE8V(p[4], a, qs); FASTSAD8V(ac_sad, p[4], d[4]); o[4] = LEFT_PREDICTED; *p[4] = (*p[4] + (cs>>1)) / cs; FASTDIFF8V(d[4], p[4]); *d[4] = mpeg4_table_clip[*d[4]]; FASTSUM8V(p[4], d[4]); *p[4] *= cs; } p[4][FASTQS] = qs; /* Cr block */ a = syntax_mpeg4->cr_pred_h[1]; /* left block */ b = syntax_mpeg4->cr_pred_h[0]; /* top left block */ c = syntax_mpeg4->cr_pred_v[mb_x]; /* top block */ /* choose between vertical and horizontal prediction */ if(abs(*a - *b) < abs(*b - *c)) { FASTAC8H(d[5], blocks[5]); FASTSCALE8H(p[5], c, qs); FASTSAD8H(ac_sad, p[5], d[5]); o[5] = TOP_PREDICTED; *p[5] = (*p[5] + (cs>>1)) / cs; FASTDIFF8H(d[5], p[5]); *d[5] = mpeg4_table_clip[*d[5]]; FASTSUM8H(p[5], d[5]); *p[5] *= cs; } else { FASTAC8V(d[5], blocks[5]); FASTSCALE8V(p[5], a, qs); FASTSAD8V(ac_sad, p[5], d[5]); o[5] = LEFT_PREDICTED; *p[5] = (*p[5] + (cs>>1)) / cs; FASTDIFF8V(d[5], p[5]); *d[5] = mpeg4_table_clip[*d[5]]; FASTSUM8V(p[5], d[5]); *p[5] *= cs; } p[5][FASTQS] = qs; /* AC/DC adjust blocks */ if(ac_sad > 0) { for(i = 0; i < 6; i++) { if(o[i] == LEFT_PREDICTED) { zigzag[i] = mpeg4_zigzag_alternate_vertical_table; COPY8V(blocks[i], d[i]); } else { zigzag[i] = mpeg4_zigzag_alternate_horizontal_table; COPY8H(blocks[i], d[i]); } } } else { zigzag[0] = zigzag[1] = zigzag[2] = zigzag[3] = zigzag[4] = zigzag[5] = mpeg4_zigzag_table; } /* check for not coded blocks */ for(j = 0; j < 6; j++) { coded[j] = 0; for(i = 1; i < 64; i++) { coded[j] |= blocks[j][i]; } } /* write mcbpc */ cbp = 0; if(coded[4]) cbp |= 2; if(coded[5]) cbp |= 1; if(syntax_mpeg4->vop_coding_type == MPEG4_I_FRAME) { if(dquant) { bitbuffer_write(&syntax_mpeg4->buffer, mcbpc_I_dq[cbp].code, mcbpc_I_dq[cbp].length); } else { bitbuffer_write(&syntax_mpeg4->buffer, mcbpc_I[cbp].code, mcbpc_I[cbp].length); } } else { if(dquant) { bitbuffer_write(&syntax_mpeg4->buffer, mcbpc_P_intra_dq[cbp].code, mcbpc_P_intra_dq[cbp].length); } else { bitbuffer_write(&syntax_mpeg4->buffer, mcbpc_P_intra[cbp].code, mcbpc_P_intra[cbp].length); } } /* AC pred flag */ if(ac_sad > 0) { bitbuffer_write(&syntax_mpeg4->buffer, 1, 1); } else { bitbuffer_write(&syntax_mpeg4->buffer, 0, 1); } /* write cbpy */ bc = cbp = 0; for(i = 0; i < 4; i++) { if(pattern & (1<buffer, cbpy[bc-1][cbp].code, cbpy[bc-1][cbp].length); /* write dquant */ if(dquant) { static const int dquant_table[5] = { 1, 0, 0, 2, 3 }; bitbuffer_write(&syntax_mpeg4->buffer, dquant_table[dquant+2], 2); } if(pattern & 1) mpeg4_block_intra(syntax,blocks[0],encode_ydc_table,*d[0],zigzag[0],coded[0]); if(pattern & 2) mpeg4_block_intra(syntax,blocks[1],encode_ydc_table,*d[1],zigzag[1],coded[1]); if(pattern & 4) mpeg4_block_intra(syntax,blocks[2],encode_ydc_table,*d[2],zigzag[2],coded[2]); if(pattern & 8) mpeg4_block_intra(syntax,blocks[3],encode_ydc_table,*d[3],zigzag[3],coded[3]); mpeg4_block_intra(syntax,blocks[4],encode_cdc_table,*d[4],zigzag[4],coded[4]); mpeg4_block_intra(syntax,blocks[5],encode_cdc_table,*d[5],zigzag[5],coded[5]); if(ac_sad >= 0) { for(i = 0; i < 6; i++) { if(o[i] == LEFT_PREDICTED) { COPY8V(blocks[i], p[i]); } else { COPY8H(blocks[i], p[i]); } } } /* TODO: fill in predictors from block */ /* update predictors */ FASTCOPY16(syntax_mpeg4->y_pred_h[0], syntax_mpeg4->y_pred_v[1][mb_x]); FASTCOPY16(syntax_mpeg4->y_pred_h[1], p[1]); FASTCOPY16(syntax_mpeg4->y_pred_h[2], p[3]); FASTCOPY16(syntax_mpeg4->y_pred_v[0][mb_x], p[2]); FASTCOPY16(syntax_mpeg4->y_pred_v[1][mb_x], syntax_mpeg4->y_pred_h[2]); FASTCOPY16(syntax_mpeg4->cb_pred_h[0], syntax_mpeg4->cb_pred_v[mb_x]); FASTCOPY16(syntax_mpeg4->cr_pred_h[0], syntax_mpeg4->cr_pred_v[mb_x]); FASTCOPY16(syntax_mpeg4->cb_pred_v[mb_x], p[4]); FASTCOPY16(syntax_mpeg4->cr_pred_v[mb_x], p[5]); FASTCOPY16(syntax_mpeg4->cb_pred_h[1], syntax_mpeg4->cb_pred_v[mb_x]); FASTCOPY16(syntax_mpeg4->cr_pred_h[1], syntax_mpeg4->cr_pred_v[mb_x]); /* mark all vectors as valid */ syntax_mpeg4->mv_pred[0].count = 64; syntax_mpeg4->mv_pred[1].count = 64; syntax_mpeg4->mv_pred[2].count = 64; syntax_mpeg4->mv_pred[3].count = 64; retval = 0; } else { /* reset predictors */ FASTCOPY16(syntax_mpeg4->y_pred_h[0], syntax_mpeg4->y_pred_v[1][mb_x]); FASTCOPY16(syntax_mpeg4->y_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_h[2], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_v[0][mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_v[1][mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[0], syntax_mpeg4->cb_pred_v[mb_x]); FASTCOPY16(syntax_mpeg4->cr_pred_h[0], syntax_mpeg4->cr_pred_v[mb_x]); FASTCOPY16(syntax_mpeg4->cb_pred_v[mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_v[mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_h[1], syntax_mpeg4->pred_default); syntax_mpeg4->mv_pred[0].count = 0; syntax_mpeg4->mv_pred[1].count = 0; syntax_mpeg4->mv_pred[2].count = 0; syntax_mpeg4->mv_pred[3].count = 0; retval = dquant; /* cancel dquant */ } /* reset motion predictors */ syntax_mpeg4->mv_pred[0].dx = syntax_mpeg4->mv_pred[0].dy = 0; syntax_mpeg4->mv_pred[1].dx = syntax_mpeg4->mv_pred[1].dy = 0; syntax_mpeg4->mv_pred[2].dx = syntax_mpeg4->mv_pred[2].dy = 0; syntax_mpeg4->mv_pred[3].dx = syntax_mpeg4->mv_pred[3].dy = 0; syntax_mpeg4->mv_pred+=4; return(retval); } static void mpeg4_block_inter(fame_syntax_t *syntax, short *block) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); short i, v; unsigned long last; fame_vlc_t const *vlc; fame_bitbuffer_t * const buffer = &syntax_mpeg4->buffer; unsigned char * data = buffer->data; unsigned long shift = buffer->shift; i = 0; last = 0; /* i < 64 checking not needed as all-zero block is not coded */ while((v = block[mpeg4_zigzag_table[i]]) == 0) i++; do { /* count zeroes */ vlc = syntax_mpeg4->inter_table + (mpeg4_table_clip[v] << 6) + i - last; last = ++i; while(i < 64 && (v = block[mpeg4_zigzag_table[i]]) == 0) i++; /* write code */ if(i != 64) { fast_bitbuffer_write(data, shift, vlc->code, vlc->length); } else { vlc += 64*511; fast_bitbuffer_write(data, shift, vlc->code, vlc->length); break; } } while(1); buffer->data = data; buffer->shift = shift; } static void inline mpeg4_write_vector(fame_syntax_t *syntax, short delta) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); /* vectors are in half-sample units */ if (delta == 0) { bitbuffer_write(&syntax_mpeg4->buffer, mb_motion_table[32].code, mb_motion_table[32].length); } else { short length; short f_code; short code; short residual; f_code = syntax_mpeg4->vop_fcode_forward; length = 16 << f_code; f_code--; if(delta >= length) delta = delta - length - length; if(delta < -length) delta = delta + length + length; if(delta > 0) { delta--; residual = delta & ((1 << f_code) - 1); code = ((delta - residual) >> f_code) + 1; } else { delta = -delta; delta--; residual = delta & ((1 << f_code) - 1); code = ((delta - residual) >> f_code) + 1; code = -code; } code += 32; bitbuffer_write(&syntax_mpeg4->buffer, mb_motion_table[code].code, mb_motion_table[code].length); if(f_code) bitbuffer_write(&syntax_mpeg4->buffer, residual, f_code); } } static int mpeg4_write_inter_mb(fame_syntax_t *syntax, int mb_x, int mb_y, short *blocks[6], unsigned char *bab, unsigned char *bab_map, fame_bab_t bab_type, int dquant, unsigned char pattern, fame_motion_vector_t *forward, fame_motion_vector_t *backward, fame_motion_coding_t motion_coding) { fame_syntax_mpeg4_t *syntax_mpeg4 = FAME_SYNTAX_MPEG4(syntax); int coded[6]; int i, j; int cbp, bc; int retval; if(syntax_mpeg4->video_object_layer_shape != MPEG4_Video_Object_Layer_Shape_Rectangular) mpeg4_write_intra_bab(syntax, mb_x, mb_y, bab, bab_map, bab_type); if(bab_type != bab_not_coded) { /* not transparent */ /* check for not coded blocks */ for(j = 0; j < 6; j++) { coded[j] = 0; if(blocks[j] != NULL) for(i = 0; i < 64; i++) { coded[j] |= blocks[j][i]; } } if(/* TODO: check slice start/end ?*/ motion_coding != motion_inter4v && forward[0].dx == 0 && forward[0].dy == 0 && !((coded[0] && (pattern & 1)) || (coded[1] && (pattern & 2)) || (coded[2] && (pattern & 4)) || (coded[3] && (pattern & 8)) || coded[4] || coded[5] )) { bitbuffer_write(&syntax_mpeg4->buffer, 1, 1); /* skip */ /* reset motion predictors */ syntax_mpeg4->mv_pred[0].dx = syntax_mpeg4->mv_pred[0].dy = 0; syntax_mpeg4->mv_pred[1].dx = syntax_mpeg4->mv_pred[1].dy = 0; syntax_mpeg4->mv_pred[2].dx = syntax_mpeg4->mv_pred[2].dy = 0; syntax_mpeg4->mv_pred[3].dx = syntax_mpeg4->mv_pred[3].dy = 0; syntax_mpeg4->mv_pred[0].count = 64; /*mark as valid*/ syntax_mpeg4->mv_pred[1].count = 64; /*mark as valid*/ syntax_mpeg4->mv_pred[2].count = 64; /*mark as valid*/ syntax_mpeg4->mv_pred[3].count = 64; /*mark as valid*/ /* reset AC/DC predictors */ FASTCOPY16(syntax_mpeg4->y_pred_h[0], syntax_mpeg4->y_pred_v[1][mb_x]); FASTCOPY16(syntax_mpeg4->y_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_h[2], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_v[0][mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_v[1][mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[0], syntax_mpeg4->cb_pred_v[mb_x]); FASTCOPY16(syntax_mpeg4->cr_pred_h[0], syntax_mpeg4->cr_pred_v[mb_x]); FASTCOPY16(syntax_mpeg4->cb_pred_v[mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_v[mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_h[1], syntax_mpeg4->pred_default); syntax_mpeg4->mv_pred+=4; /* skipped macroblock */ return(dquant); } else { bitbuffer_write(&syntax_mpeg4->buffer, 0, 1); } /* write mcbpc */ cbp = 0; if(coded[4]) cbp |= 2; if(coded[5]) cbp |= 1; if(motion_coding == motion_inter4v) { bitbuffer_write(&syntax_mpeg4->buffer, mcbpc_P_inter4v[cbp].code, mcbpc_P_inter4v[cbp].length); } else { if(dquant) { bitbuffer_write(&syntax_mpeg4->buffer, mcbpc_P_inter_dq[cbp].code, mcbpc_P_inter_dq[cbp].length); } else { bitbuffer_write(&syntax_mpeg4->buffer, mcbpc_P_inter[cbp].code, mcbpc_P_inter[cbp].length); } } /* write cbpy */ bc = cbp = 0; for(i = 0; i < 4; i++) { if(pattern & (1<buffer, cbpy[bc-1][cbp].code, cbpy[bc-1][cbp].length); /* write dquant */ if(motion_coding != motion_inter4v && dquant) { static const int dquant_table[5] = { 1, 0, 0, 2, 3 }; bitbuffer_write(&syntax_mpeg4->buffer, dquant_table[dquant+2], 2); syntax_mpeg4->quant_scale += dquant; retval = 0; } else { retval = dquant; } /* motion vector forward */ if(motion_coding == motion_inter4v) { /* number of vectors depends on shape */ if(pattern & 1) { mpeg4_write_vector(syntax, forward[0].dx-syntax_mpeg4->mv_pred[0].dx); mpeg4_write_vector(syntax, forward[0].dy-syntax_mpeg4->mv_pred[0].dy); } if(pattern & 2) { syntax->predict_vector(syntax, mb_x, mb_y, 1, forward); mpeg4_write_vector(syntax, forward[1].dx-syntax_mpeg4->mv_pred[1].dx); mpeg4_write_vector(syntax, forward[1].dy-syntax_mpeg4->mv_pred[1].dy); } if(pattern & 4) { syntax->predict_vector(syntax, mb_x, mb_y, 2, forward); mpeg4_write_vector(syntax, forward[2].dx-syntax_mpeg4->mv_pred[2].dx); mpeg4_write_vector(syntax, forward[2].dy-syntax_mpeg4->mv_pred[2].dy); } if(pattern & 8) { syntax->predict_vector(syntax, mb_x, mb_y, 3, forward); mpeg4_write_vector(syntax, forward[3].dx-syntax_mpeg4->mv_pred[3].dx); mpeg4_write_vector(syntax, forward[3].dy-syntax_mpeg4->mv_pred[3].dy); } } else { mpeg4_write_vector(syntax, forward[0].dx-syntax_mpeg4->mv_pred[0].dx); mpeg4_write_vector(syntax, forward[0].dy-syntax_mpeg4->mv_pred[0].dy); } /* Y (0,0) block */ if((pattern & 1) && coded[0]) mpeg4_block_inter(syntax, blocks[0]); /* Y (0,1) block */ if((pattern & 2) && coded[1]) mpeg4_block_inter(syntax, blocks[1]); /* Y (1,0) block */ if((pattern & 4) && coded[2]) mpeg4_block_inter(syntax, blocks[2]); /* Y (1,1) block */ if((pattern & 8) && coded[3]) mpeg4_block_inter(syntax, blocks[3]); /* Cb block */ if(coded[4]) mpeg4_block_inter(syntax, blocks[4]); /* Cr block */ if(coded[5]) mpeg4_block_inter(syntax, blocks[5]); if(motion_coding == motion_inter4v) { /* pad motion vectors */ switch(pattern) { /* 10 */ /* 00 */ case 0x1: forward[1].dx = forward[0].dx; forward[1].dy = forward[0].dy; forward[2].dx = forward[0].dx; forward[2].dy = forward[0].dy; forward[3].dx = forward[0].dx; forward[3].dy = forward[0].dy; break; /* 01 */ /* 00 */ case 0x2: forward[0].dx = forward[1].dx; forward[0].dy = forward[1].dy; forward[2].dx = forward[1].dx; forward[2].dy = forward[1].dy; forward[3].dx = forward[1].dx; forward[3].dy = forward[1].dy; break; /* 11 */ /* 00 */ case 0x3: forward[2].dx = forward[0].dx; forward[2].dy = forward[0].dy; forward[3].dx = forward[1].dx; forward[3].dy = forward[1].dy; break; /* 00 */ /* 10 */ case 0x4: forward[0].dx = forward[2].dx; forward[0].dy = forward[2].dy; forward[1].dx = forward[2].dx; forward[1].dy = forward[2].dy; forward[3].dx = forward[2].dx; forward[3].dy = forward[2].dy; break; /* 10 */ /* 10 */ case 0x5: forward[1].dx = forward[0].dx; forward[1].dy = forward[0].dy; forward[3].dx = forward[2].dx; forward[3].dy = forward[2].dy; break; /* 01 */ /* 10 */ case 0x6: forward[0].dx = forward[1].dx; forward[0].dy = forward[1].dy; forward[3].dx = forward[2].dx; forward[3].dy = forward[2].dy; break; /* 11 */ /* 10 */ case 0x7: forward[3].dx = forward[2].dx; forward[3].dy = forward[2].dy; break; /* 00 */ /* 01 */ case 0x8: forward[0].dx = forward[3].dx; forward[0].dy = forward[3].dy; forward[1].dx = forward[3].dx; forward[1].dy = forward[3].dy; forward[2].dx = forward[3].dx; forward[2].dy = forward[3].dy; break; /* 10 */ /* 01 */ case 0x9: forward[1].dx = forward[0].dx; forward[1].dy = forward[0].dy; forward[2].dx = forward[3].dx; forward[2].dy = forward[3].dy; break; /* 01 */ /* 01 */ case 0xa: forward[0].dx = forward[1].dx; forward[0].dy = forward[1].dy; forward[2].dx = forward[3].dx; forward[2].dy = forward[3].dy; break; /* 11 */ /* 01 */ case 0xb: forward[2].dx = forward[3].dx; forward[2].dy = forward[3].dy; break; /* 00 */ /* 11 */ case 0xc: forward[0].dx = forward[2].dx; forward[0].dy = forward[2].dy; forward[1].dx = forward[3].dx; forward[1].dy = forward[3].dy; break; /* 10 */ /* 11 */ case 0xd: forward[1].dx = forward[0].dx; forward[1].dy = forward[0].dy; break; /* 01 */ /* 11 */ case 0xe: forward[0].dx = forward[1].dx; forward[0].dy = forward[1].dy; break; } syntax_mpeg4->mv_pred[0].count = 64; /* valid */ syntax_mpeg4->mv_pred[1].count = 64; /* valid */ syntax_mpeg4->mv_pred[2].count = 64; /* valid */ syntax_mpeg4->mv_pred[3].count = 64; /* valid */ } else { /* mark all vectors as valid */ syntax_mpeg4->mv_pred[0].count = 64; syntax_mpeg4->mv_pred[1].count = 64; syntax_mpeg4->mv_pred[2].count = 64; syntax_mpeg4->mv_pred[3].count = 64; } /* update motion prediction */ syntax_mpeg4->mv_pred[0].dx = forward[0].dx; syntax_mpeg4->mv_pred[0].dy = forward[0].dy; syntax_mpeg4->mv_pred[1].dx = forward[1].dx; syntax_mpeg4->mv_pred[1].dy = forward[1].dy; syntax_mpeg4->mv_pred[2].dx = forward[2].dx; syntax_mpeg4->mv_pred[2].dy = forward[2].dy; syntax_mpeg4->mv_pred[3].dx = forward[3].dx; syntax_mpeg4->mv_pred[3].dy = forward[3].dy; } else { /* reset motion predictors */ syntax_mpeg4->mv_pred[0].dx = syntax_mpeg4->mv_pred[0].dy = 0; syntax_mpeg4->mv_pred[1].dx = syntax_mpeg4->mv_pred[1].dy = 0; syntax_mpeg4->mv_pred[2].dx = syntax_mpeg4->mv_pred[2].dy = 0; syntax_mpeg4->mv_pred[3].dx = syntax_mpeg4->mv_pred[3].dy = 0; syntax_mpeg4->mv_pred[0].count = 0; syntax_mpeg4->mv_pred[1].count = 0; syntax_mpeg4->mv_pred[2].count = 0; syntax_mpeg4->mv_pred[3].count = 0; retval = dquant; /* cancel dquant */ } /* reset AC/DC predictors */ FASTCOPY16(syntax_mpeg4->y_pred_h[0], syntax_mpeg4->y_pred_v[1][mb_x]); FASTCOPY16(syntax_mpeg4->y_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_h[2], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_v[0][mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->y_pred_v[1][mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[0], syntax_mpeg4->cb_pred_v[mb_x]); FASTCOPY16(syntax_mpeg4->cr_pred_h[0], syntax_mpeg4->cr_pred_v[mb_x]); FASTCOPY16(syntax_mpeg4->cb_pred_v[mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_v[mb_x], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cb_pred_h[1], syntax_mpeg4->pred_default); FASTCOPY16(syntax_mpeg4->cr_pred_h[1], syntax_mpeg4->pred_default); syntax_mpeg4->mv_pred+=4; return(retval); } static void mpeg4_compute_chrominance_vectors(fame_syntax_t *syntax, fame_motion_vector_t *vectors, unsigned char pattern) { int x, y, k; static int const rounding_16[16] = {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2}; static int const rounding_12[12] = { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2}; static int const rounding_8[8] = {0, 0, 1, 1, 1, 1, 1, 2 }; static int const rounding_4[4] = {0, 1, 1, 1 }; /* rounding depends on shape */ x = 0; y = 0; k = 0; if(pattern & 1) { x += vectors[0].dx; y += vectors[0].dy; k++; } if(pattern & 2) { x += vectors[1].dx; y += vectors[1].dy; k++; } if(pattern & 4) { x += vectors[2].dx; y += vectors[2].dy; k++; } if(pattern & 8) { x += vectors[3].dx; y += vectors[3].dy; k++; } switch(k) { case 1: if(x > 0) vectors[4].dx = ((x >> 2) << 1) + rounding_4[x & 3]; else vectors[4].dx = -((((-x) >> 2) << 1) + rounding_4[(-x) & 3]); if(y > 0) vectors[4].dy = ((y >> 2) << 1) + rounding_4[y & 3]; else vectors[4].dy = -((((-y) >> 2) << 1) + rounding_4[(-y) & 3]); break; case 2: if(x > 0) vectors[4].dx = ((x >> 3) << 1) + rounding_8[x & 7]; else vectors[4].dx = -((((-x) >> 3) << 1) + rounding_8[(-x) & 7]); if(y > 0) vectors[4].dy = ((y >> 3) << 1) + rounding_8[y & 7]; else vectors[4].dy = -((((-y) >> 3) << 1) + rounding_8[(-y) & 7]); break; case 3: if(x > 0) vectors[4].dx = ((x / 12) << 1) + rounding_12[x % 12]; else vectors[4].dx = -((((-x) / 12) << 1) + rounding_12[(-x) % 12]); if(y > 0) vectors[4].dy = ((y / 12) << 1) + rounding_12[y % 12]; else vectors[4].dy = -((((-y) / 12) << 1) + rounding_12[(-y) % 12]); break; case 4: if(x > 0) vectors[4].dx = ((x >> 4) << 1) + rounding_16[x & 15]; else vectors[4].dx = -((((-x) >> 4) << 1) + rounding_16[(-x) & 15]); if(y > 0) vectors[4].dy = ((y >> 4) << 1) + rounding_16[y & 15]; else vectors[4].dy = -((((-y) >> 4) << 1) + rounding_16[(-y) & 15]); break; } vectors[5].dx = vectors[4].dx; vectors[5].dy = vectors[4].dy; }