/* libfame - Fast Assembly MPEG Encoder Library Copyright (C) 2000-2001 Vivien Chappelier 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 #include "fame.h" #include "fame_malloc.h" #include "fame_profile.h" #include "fame_profile_mpeg.h" #include "fame_encoder.h" #include "fame_decoder.h" #include "fame_motion.h" #include "fame_syntax.h" #include "fame_shape.h" #include "fame_monitor.h" static void profile_mpeg_init(fame_profile_t *profile, fame_context_t *context, fame_parameters_t *params, unsigned char *buffer, unsigned int size); static void profile_mpeg_enter(fame_profile_t *profile, fame_yuv_t *yuv, unsigned char *shape); static int profile_mpeg_encode(fame_profile_t *profile); static void profile_mpeg_leave(fame_profile_t *profile, fame_frame_statistics_t *stats); static int profile_mpeg_close(fame_profile_t *profile); FAME_CONSTRUCTOR(fame_profile_mpeg_t) { FAME_OBJECT(this)->name = "MPEG profile"; FAME_PROFILE(this)->init = profile_mpeg_init; FAME_PROFILE(this)->enter = profile_mpeg_enter; FAME_PROFILE(this)->encode = profile_mpeg_encode; FAME_PROFILE(this)->leave = profile_mpeg_leave; FAME_PROFILE(this)->close = profile_mpeg_close; this->encoder_flags = 0; this->decoder_flags = 0; this->motion_flags = 0; this->syntax_flags = 0; this->shape_flags = 0; this->rate_flags = 0; this->monitor_flags = 0; return(this); } /* profile_mpeg_init */ /* */ /* Description: */ /* Initialize the profile. */ /* */ /* Arguments: */ /* fame_profile_t *profile: the profile to initialize */ /* fame_parameters_t *params: the parameters for initialization */ /* unsigned char *buffer: the buffer to output data */ /* unsigned int size: the size of the output buffer */ /* */ /* Return value: */ /* None. */ static void profile_mpeg_init(fame_profile_t *profile, fame_context_t *context, fame_parameters_t *params, unsigned char *buffer, unsigned int size) { fame_profile_mpeg_t *profile_mpeg = FAME_PROFILE_MPEG(profile); int i, j; profile_mpeg->width = params->width; profile_mpeg->height = params->height; profile_mpeg->coding = strdup(params->coding); profile_mpeg->quant_scale = 1 + (30*(100-params->quality)+50)/100; profile_mpeg->bitrate = params->bitrate; profile_mpeg->slices_per_frame = params->slices_per_frame; profile_mpeg->frames_per_gop = strlen(profile_mpeg->coding); profile_mpeg->frames_per_sequence = params->frames_per_sequence; profile_mpeg->total_frames = params->total_frames; profile_mpeg->lines_per_slice = ((((params->height + 15) >> 4) + profile_mpeg->slices_per_frame - 1) / profile_mpeg->slices_per_frame); profile_mpeg->slice_number = 0; profile_mpeg->frame_number = 0; profile_mpeg->gop_number = 0; profile_mpeg->fps_num = params->frame_rate_num; profile_mpeg->fps_den = params->frame_rate_den; profile_mpeg->alpha_th = 255*(100 - params->shape_quality)/100; if(params->search_range) { profile_mpeg->search_range = params->search_range; profile_mpeg->search_range_adaptive = 0; } else { profile_mpeg->search_range = 32; profile_mpeg->search_range_adaptive = 1; } profile_mpeg->intra_matrix = NULL; profile_mpeg->inter_matrix = NULL; profile_mpeg->verbose = params->verbose; profile_mpeg->rounding = 0; /* Get the components */ profile_mpeg->decoder = (fame_decoder_t *) fame_get_object(context, "decoder"); profile_mpeg->encoder = (fame_encoder_t *) fame_get_object(context, "encoder"); profile_mpeg->motion = (fame_motion_t *) fame_get_object(context, "motion"); profile_mpeg->syntax = (fame_syntax_t *) fame_get_object(context, "syntax"); profile_mpeg->shape = (fame_shape_t *) fame_get_object(context, "shape"); profile_mpeg->rate = (fame_rate_t *) fame_get_object(context, "rate"); profile_mpeg->monitor = (fame_monitor_t *) fame_get_object(context, "monitor"); /* VBR/CBR coding */ if(profile_mpeg->bitrate == 0) profile_mpeg->rate = NULL; /* don't need rate control */ /* Initialize buffer */ memset(buffer, 0, size); profile_mpeg->buffer = buffer; profile_mpeg->size = size; profile_mpeg->dirty = 0; /* Allocate reference frame ring */ for(j = 0; j < 2; j++) /* 2 references */ for(i = 0; i < 4; i++) { /* 4 planes per reference (interpolation) */ profile_mpeg->ref[j][i] = (fame_yuv_t *) fame_malloc(sizeof(fame_yuv_t)); profile_mpeg->ref[j][i]->w = profile_mpeg->width; profile_mpeg->ref[j][i]->h = profile_mpeg->height; profile_mpeg->ref[j][i]->p = profile_mpeg->width+32; profile_mpeg->ref[j][i]->y = (unsigned char *) fame_malloc((profile_mpeg->width+32)* (profile_mpeg->height+32)*12/8); profile_mpeg->ref[j][i]->u = profile_mpeg->ref[j][i]->y + (profile_mpeg->width+32)*(profile_mpeg->height+32); profile_mpeg->ref[j][i]->v = profile_mpeg->ref[j][i]->u + (profile_mpeg->width+32)*(profile_mpeg->height+32)/4; /* add offset to beggining of picture (padding) */ profile_mpeg->ref[j][i]->y += 16*(profile_mpeg->width+32)+16; profile_mpeg->ref[j][i]->u += 8*(profile_mpeg->width+32)/2+8; profile_mpeg->ref[j][i]->v += 8*(profile_mpeg->width+32)/2+8; } /* Allocate reconstructed shape and BAB map */ if(profile_mpeg->shape) { profile_mpeg->ref_shape = (unsigned char *) fame_malloc(profile_mpeg->width*profile_mpeg->height); profile_mpeg->bab_map = (unsigned char *) fame_malloc(((profile_mpeg->width >> 4) + 2)* ((profile_mpeg->height >> 4) + 2)); } else { profile_mpeg->ref_shape = NULL; profile_mpeg->bab_map = NULL; } /* Initialize reference pointers */ profile_mpeg->past = 1; profile_mpeg->future = 0; profile_mpeg->current = 1; /* Initialize motion estimation */ if(profile_mpeg->motion && profile_mpeg->motion->init) profile_mpeg->motion->init(profile_mpeg->motion, (profile_mpeg->width >> 4), (profile_mpeg->height >> 4), FAME_PROFILE_MPEG(profile)->motion_flags); /* Initialize the syntax */ if(profile_mpeg->syntax && profile_mpeg->syntax->init) profile_mpeg->syntax->init(profile_mpeg->syntax, (profile_mpeg->width >> 4), (profile_mpeg->height >> 4), &profile_mpeg->intra_matrix, &profile_mpeg->inter_matrix, profile_mpeg->intra_dc_y_scale_table, profile_mpeg->intra_dc_c_scale_table, &profile_mpeg->mismatch, FAME_PROFILE_MPEG(profile)->syntax_flags); /* Initialize the encoder */ if(profile_mpeg->encoder && profile_mpeg->encoder->init) profile_mpeg->encoder->init(profile_mpeg->encoder, profile_mpeg->width, profile_mpeg->height, profile_mpeg->intra_matrix, profile_mpeg->inter_matrix, profile_mpeg->intra_dc_y_scale_table, profile_mpeg->intra_dc_c_scale_table, profile_mpeg->mismatch); /* Initialize the decoder */ if(profile_mpeg->decoder && profile_mpeg->decoder->init) profile_mpeg->decoder->init(profile_mpeg->decoder, profile_mpeg->width, profile_mpeg->height, profile_mpeg->intra_matrix, profile_mpeg->inter_matrix, profile_mpeg->intra_dc_y_scale_table, profile_mpeg->intra_dc_c_scale_table, profile_mpeg->mismatch); /* Initialize the shape coder */ if(profile_mpeg->shape && profile_mpeg->shape->init) profile_mpeg->shape->init(profile_mpeg->shape, (profile_mpeg->width >> 4), (profile_mpeg->height >> 4), FAME_PROFILE_MPEG(profile)->shape_flags); /* Initialize statistics monitoring */ if(profile_mpeg->monitor && profile_mpeg->monitor->init) profile_mpeg->monitor->init(profile_mpeg->monitor, params->retrieve_cb, (profile_mpeg->width >> 4), (profile_mpeg->height >> 4), profile_mpeg->total_frames, FAME_PROFILE_MPEG(profile)->monitor_flags); /* Initialize rate control */ if(profile_mpeg->rate && profile_mpeg->rate->init) profile_mpeg->rate->init(profile_mpeg->rate, (profile_mpeg->width >> 4), (profile_mpeg->height >> 4), profile_mpeg->bitrate/ profile_mpeg->fps_num*profile_mpeg->fps_den, profile_mpeg->coding, profile_mpeg->monitor->frame_stats_list, &(profile_mpeg->monitor->global_stats), FAME_PROFILE_MPEG(profile)->rate_flags); } /* profile_mpeg_enter */ /* */ /* Description: */ /* Start encoding a picture. */ /* */ /* Arguments: */ /* fame_profile_t * profile: the profile handle returned by fame_open */ /* fame_yuv_t * yuv: the input frame in raw YUV format (YV12 planar) */ /* unsigned char * mask: the input mask (0 = transparent, 255 = opaque) */ /* */ /* Return value: */ /* None. */ static void profile_mpeg_enter(fame_profile_t *profile, fame_yuv_t *yuv, unsigned char *shape) { fame_profile_mpeg_t *profile_mpeg = FAME_PROFILE_MPEG(profile); /* Update stats and choose coding mode */ profile_mpeg->current_coding = profile_mpeg->coding[profile_mpeg->frame_number % strlen(profile_mpeg->coding)]; profile_mpeg->next_coding = profile_mpeg->coding[(profile_mpeg->frame_number + 1) % strlen(profile_mpeg->coding)]; if (profile_mpeg->monitor && profile_mpeg->monitor->current_frame_stats) profile_mpeg->frame_stats = profile_mpeg->monitor->current_frame_stats; else profile_mpeg->frame_stats = NULL; /* Clear BAB map */ if(profile_mpeg->bab_map) memset(profile_mpeg->bab_map, bab_not_coded, ((profile_mpeg->width >> 4) + 2)*((profile_mpeg->height >> 4) + 2)); /* input pitch = input width if not set */ if(yuv->p == 0) yuv->p = yuv->w; /* Initialize statistics */ if(profile_mpeg->monitor && profile_mpeg->monitor->enter) profile_mpeg->monitor->enter(profile_mpeg->monitor, profile_mpeg->frame_number, profile_mpeg->ref[profile_mpeg->future], yuv, shape, &profile_mpeg->current_coding); /* Initialize syntax buffer */ if(profile_mpeg->syntax && profile_mpeg->syntax->use) profile_mpeg->syntax->use(profile_mpeg->syntax, profile_mpeg->buffer, profile_mpeg->dirty); /* Generate sequence */ if(profile_mpeg->frame_number % profile_mpeg->frames_per_sequence == 0) if(profile_mpeg->syntax && profile_mpeg->syntax->start_sequence) profile_mpeg->syntax->start_sequence(profile_mpeg->syntax, profile_mpeg->width, profile_mpeg->height, profile_mpeg->fps_num, profile_mpeg->fps_den, profile_mpeg->size* profile_mpeg->frames_per_gop, profile_mpeg->bitrate); /* Generate group of pictures */ if(profile_mpeg->frame_number % profile_mpeg->frames_per_gop == 0) if(profile_mpeg->syntax && profile_mpeg->syntax->start_GOP) profile_mpeg->syntax->start_GOP(profile_mpeg->syntax, profile_mpeg->frame_number); /* TODO: find bounding box */ profile_mpeg->bounding_box.x = 0; profile_mpeg->bounding_box.y = 0; profile_mpeg->bounding_box.w = profile_mpeg->width; profile_mpeg->bounding_box.h = profile_mpeg->height; /* Reset rounding control */ if(profile_mpeg->current_coding == 'I') profile_mpeg->rounding = 0; /* Generate picture */ if(profile_mpeg->syntax && profile_mpeg->syntax->start_picture) profile_mpeg->syntax->start_picture(profile_mpeg->syntax, profile_mpeg->current_coding, profile_mpeg->frame_number%profile_mpeg->frames_per_gop, &profile_mpeg->bounding_box, profile_mpeg->rounding, profile_mpeg->search_range); /* Enter the encoder */ if(profile_mpeg->encoder && profile_mpeg->encoder->enter) profile_mpeg->encoder->enter(profile_mpeg->encoder, profile_mpeg->ref[profile_mpeg->past], profile_mpeg->ref[profile_mpeg->current], profile_mpeg->ref[profile_mpeg->future], yuv, profile_mpeg->ref_shape); if(profile_mpeg->decoder && profile_mpeg->decoder->enter) profile_mpeg->decoder->enter(profile_mpeg->decoder, profile_mpeg->ref[profile_mpeg->past], profile_mpeg->ref[profile_mpeg->current], profile_mpeg->ref[profile_mpeg->future], yuv, profile_mpeg->ref_shape); if(profile_mpeg->shape && profile_mpeg->shape->enter) profile_mpeg->shape->enter(profile_mpeg->shape, shape, profile_mpeg->ref_shape, profile_mpeg->alpha_th); if(profile_mpeg->motion && profile_mpeg->motion->enter) profile_mpeg->motion->enter(profile_mpeg->motion, profile_mpeg->ref[profile_mpeg->future], yuv, profile_mpeg->ref_shape, profile_mpeg->search_range); if(profile_mpeg->rate && profile_mpeg->rate->enter) profile_mpeg->rate->enter(profile_mpeg->rate, profile_mpeg->ref[profile_mpeg->future], yuv, profile_mpeg->ref_shape, profile_mpeg->current_coding, profile_mpeg->frame_stats); /* estimate quantiser scale for frame */ if(profile_mpeg->rate && profile_mpeg->rate->global_estimation) profile_mpeg->quant_scale = profile_mpeg->rate->global_estimation(profile_mpeg->rate); /* initialize block count */ profile_mpeg->intra = profile_mpeg->inter = 0; /* initialize slice offset */ profile_mpeg->slice_start = 0; profile_mpeg->total = 0; } /* profile_mpeg_encode */ /* */ /* Description: */ /* Encode a single slice. */ /* */ /* Arguments: */ /* fame_profile_t * profile: the profile handle returned by fame_open */ /* */ /* Return value: */ /* int : the number of bytes written to buffer */ static int profile_mpeg_encode(fame_profile_t *profile) { fame_profile_mpeg_t *profile_mpeg = FAME_PROFILE_MPEG(profile); int x, y; int slice_end; /* the 4 Y and 2 C blocks in a macroblock */ short * blocks[6]; /* the binary alpha block */ unsigned char *bab; fame_bab_t bab_type; fame_motion_coding_t motion_coding; fame_motion_vector_t forward[6]; unsigned char pattern; int dquant; int mv_count = 0; int mv_norm = 0; int quant_sum = 0; /* Test for end of frame */ if(profile_mpeg->slice_start >= (profile_mpeg->height >> 4)) return(0); /* Clear syntax buffer */ if(profile_mpeg->slice_start != 0) { if(profile_mpeg->syntax && profile_mpeg->syntax->use) profile_mpeg->syntax->use(profile_mpeg->syntax, profile_mpeg->buffer, profile_mpeg->dirty); profile_mpeg->dirty = 0; } bab_type = bab_all_coded; motion_coding = motion_intra; pattern = 0x0f; /* all blocks coded */ /* Generate slice */ if(profile_mpeg->syntax && profile_mpeg->syntax->start_slice) profile_mpeg->syntax->start_slice(profile_mpeg->syntax, profile_mpeg->slice_start, profile_mpeg->lines_per_slice* (profile_mpeg->width>>4), profile_mpeg->quant_scale); slice_end = fame_min(profile_mpeg->height >> 4, profile_mpeg->slice_start+profile_mpeg->lines_per_slice); /* Encode macroblocks */ for (y = profile_mpeg->slice_start; y < slice_end; y++) for (x = 0; x < (profile_mpeg->width >> 4); x++) { /* code shape */ if(profile_mpeg->shape && profile_mpeg->shape->encode_intra_shape) bab_type = profile_mpeg->shape->encode_intra_shape(profile_mpeg->shape, x, y, &bab, &pattern); /* compensate motion */ if(profile_mpeg->current_coding == 'P') { if(profile_mpeg->syntax && profile_mpeg->syntax->predict_vector) profile_mpeg->syntax->predict_vector(profile_mpeg->syntax, x, y, 0, forward); if(profile_mpeg->motion && profile_mpeg->motion->estimation) motion_coding = profile_mpeg->motion->estimation(profile_mpeg->motion, x, y, forward, profile_mpeg->quant_scale); /* U and V vectors */ if(profile_mpeg->syntax && profile_mpeg->syntax->compute_chrominance_vectors) profile_mpeg->syntax->compute_chrominance_vectors(profile_mpeg->syntax, forward, pattern); } else motion_coding = motion_intra; /* adaptive quantization */ if(profile_mpeg->rate && profile_mpeg->rate->local_estimation) dquant = profile_mpeg->rate->local_estimation(profile_mpeg->rate, x, y, blocks, motion_coding); else dquant = 0; if(motion_coding == motion_intra) { profile_mpeg->intra++; /* Code intra macroblock */ if(profile_mpeg->encoder && profile_mpeg->encoder->encode_intra_mb && bab_type != bab_not_coded) profile_mpeg->encoder->encode_intra_mb(profile_mpeg->encoder, x, y, blocks, profile_mpeg->quant_scale, bab_type); if(profile_mpeg->next_coding != 'I') if(profile_mpeg->decoder && profile_mpeg->decoder->reconstruct_intra_mb && bab_type != bab_not_coded) profile_mpeg->decoder->reconstruct_intra_mb(profile_mpeg->decoder, x, y, blocks, profile_mpeg->quant_scale, bab_type); /* Write macroblock */ if(profile_mpeg->syntax && profile_mpeg->syntax->write_intra_mb) profile_mpeg->quant_scale -= /* dquant may be cancelled */ profile_mpeg->syntax->write_intra_mb(profile_mpeg->syntax, x, y, blocks, bab, profile_mpeg->bab_map, bab_type, dquant, pattern); } else { profile_mpeg->inter++; /* TODO: check for coded in syntax macroblock depending on error */ /* of motion estimation, for inter blocks only. */ if(profile_mpeg->search_range_adaptive) { if(motion_coding == motion_inter4v) { mv_count += 4; mv_norm += forward[0].dx*forward[0].dx + forward[0].dy*forward[0].dy; mv_norm += forward[1].dx*forward[1].dx + forward[1].dy*forward[1].dy; mv_norm += forward[2].dx*forward[2].dx + forward[2].dy*forward[2].dy; mv_norm += forward[3].dx*forward[3].dx + forward[3].dy*forward[3].dy; } else { mv_count++; mv_norm += forward[0].dx*forward[0].dx + forward[0].dy*forward[0].dy; } } /* Code inter macroblock */ if(profile_mpeg->encoder && profile_mpeg->encoder->encode_inter_mb && bab_type != bab_not_coded) profile_mpeg->encoder->encode_inter_mb(profile_mpeg->encoder, x, y, blocks, forward, NULL, motion_coding, profile_mpeg->quant_scale, bab_type); if(profile_mpeg->next_coding != 'I') if(profile_mpeg->decoder && profile_mpeg->decoder->reconstruct_inter_mb && bab_type != bab_not_coded) profile_mpeg->decoder->reconstruct_inter_mb(profile_mpeg->decoder, x, y, blocks, forward, NULL, motion_coding, profile_mpeg->quant_scale, bab_type); /* Write macroblock */ if(profile_mpeg->syntax && profile_mpeg->syntax->write_inter_mb) profile_mpeg->quant_scale -= /* dquant may be cancelled */ profile_mpeg->syntax->write_inter_mb(profile_mpeg->syntax, x, y, blocks, bab, profile_mpeg->bab_map, bab_type, dquant, pattern, forward, NULL, motion_coding); } quant_sum += profile_mpeg->quant_scale; } #ifdef HAS_MMX asm("emms"); #endif if(mv_count) { /* adapt search range according to MV standard deviation */ mv_norm /= mv_count; mv_norm = (int) sqrt(mv_norm); if(profile_mpeg->search_range < 3*mv_norm && profile_mpeg->search_range < 1024) { profile_mpeg->search_range <<= 1; } else if(profile_mpeg->search_range > 6*mv_norm && profile_mpeg->search_range > 16) { profile_mpeg->search_range >>= 1; } } /* End of slice */ if(profile_mpeg->syntax && profile_mpeg->syntax->end_slice) profile_mpeg->syntax->end_slice(profile_mpeg->syntax); profile_mpeg->quant_avg = (float) quant_sum / ((slice_end - profile_mpeg->slice_start) * (profile_mpeg->width >> 4)); /* Return the number of bytes encoded */ if(profile_mpeg->syntax && profile_mpeg->syntax->flush) profile_mpeg->dirty = profile_mpeg->syntax->flush(profile_mpeg->syntax); else profile_mpeg->dirty = 0; profile_mpeg->total += profile_mpeg->dirty; profile_mpeg->slice_start = slice_end; return(profile_mpeg->dirty); } /* profile_mpeg_leave */ /* */ /* Description: */ /* Finish encoding a picture. */ /* */ /* Arguments: */ /* fame_profile_t * profile: the profile handle returned by fame_open */ /* fame_frame_statistics_t * stats: information about the encoding */ /* */ /* Return value: */ /* None. */ static void profile_mpeg_leave(fame_profile_t *profile, fame_frame_statistics_t *stats) { fame_profile_mpeg_t *profile_mpeg = FAME_PROFILE_MPEG(profile); /* Pad and interpolate for half-pel estimation/compensation */ if(profile_mpeg->motion->flags & FAME_MOTION_FLIP_ROUNDING) profile_mpeg->rounding ^= 1; if(profile_mpeg->next_coding != 'I' && (profile_mpeg->motion->flags & FAME_MOTION_SUBPEL_SEARCH)) if(profile_mpeg->decoder && profile_mpeg->decoder->interpolate) profile_mpeg->decoder->interpolate(profile_mpeg->decoder, profile_mpeg->rounding); if(profile_mpeg->next_coding != 'I' && (profile_mpeg->shape || (profile_mpeg->motion->flags & FAME_MOTION_UNRESTRICTED_SEARCH))) if(profile_mpeg->decoder && profile_mpeg->decoder->pad) profile_mpeg->decoder->pad(profile_mpeg->decoder, profile_mpeg->bab_map, &profile_mpeg->bounding_box); #undef DEBUG_WRITE_RECONSTRUCTED_FRAMES #ifdef DEBUG_WRITE_RECONSTRUCTED_FRAMES /* Write reconstructed frame to standard output */ fwrite(profile_mpeg->ref[profile_mpeg->current][1]->y - (16*(profile_mpeg->width+32)+16), (profile_mpeg->width+32)*(profile_mpeg->height+32), 1, stdout); fwrite(profile_mpeg->ref[profile_mpeg->current][1]->u - (8*(profile_mpeg->width+32)/2+8), (profile_mpeg->width+32)*(profile_mpeg->height+32)/4, 1, stdout); fwrite(profile_mpeg->ref[profile_mpeg->current][1]->v - (8*(profile_mpeg->width+32)/2+8), (profile_mpeg->width+32)*(profile_mpeg->height+32)/4, 1, stdout); #endif /* Leave the encoder */ if(profile_mpeg->encoder && profile_mpeg->encoder->leave) profile_mpeg->encoder->leave(profile_mpeg->encoder); /* Leave the decoder */ if(profile_mpeg->decoder && profile_mpeg->decoder->leave) profile_mpeg->decoder->leave(profile_mpeg->decoder); /* Leave the motion */ if(profile_mpeg->motion && profile_mpeg->motion->leave) profile_mpeg->motion->leave(profile_mpeg->motion); /* Rotate reference ring */ switch(profile_mpeg->current_coding) { case 'I': case 'P': profile_mpeg->past = profile_mpeg->future; profile_mpeg->future = profile_mpeg->current; profile_mpeg->current = !profile_mpeg->current; break; case 'B': break; default: break; } /* Increment frame number */ profile_mpeg->frame_number++; /* Update rate control */ if(profile_mpeg->rate && profile_mpeg->rate->leave) profile_mpeg->rate->leave(profile_mpeg->rate, profile_mpeg->total * 8); /* Show picture info */ if(profile_mpeg->verbose) { if(profile_mpeg->intra+profile_mpeg->inter) { FAME_INFO("inter/intra %3d%% ", 100*profile_mpeg->inter/ (profile_mpeg->intra+profile_mpeg->inter)); } FAME_INFO("%dkbits/s quality %.02f%% range %d %c frame #%d\033[K\r", (profile_mpeg->total * profile_mpeg->fps_num * 8) / (profile_mpeg->fps_den * 1000), (31 - profile_mpeg->quant_avg) * 100 / 30, profile_mpeg->search_range, profile_mpeg->current_coding, profile_mpeg->frame_number); } if(profile_mpeg->monitor && profile_mpeg->monitor->leave) profile_mpeg->monitor->leave(profile_mpeg->monitor, profile_mpeg->total * 8, profile_mpeg->quant_avg); if(stats) *stats = *profile_mpeg->frame_stats; } /* profile_mpeg_close */ /* */ /* Description: */ /* Flush remaining encoded data and cleanup everything. */ /* */ /* Arguments: */ /* fame_profile_t * profile: the profile handle returned by fame_open */ /* */ /* Return value: */ /* int : the number of bytes written to buffer */ static int profile_mpeg_close(fame_profile_t *profile) { fame_profile_mpeg_t *profile_mpeg = FAME_PROFILE_MPEG(profile); int i, j; int bytes_written; /* Initialize syntax buffer */ if(profile_mpeg->syntax && profile_mpeg->syntax->use) profile_mpeg->syntax->use(profile_mpeg->syntax, profile_mpeg->buffer, profile_mpeg->dirty); /* Generate the sequence ender code */ if(profile_mpeg->syntax && profile_mpeg->syntax->end_sequence) profile_mpeg->syntax->end_sequence(profile_mpeg->syntax); /* Flush the syntax buffer */ if(profile_mpeg->syntax && profile_mpeg->syntax->flush) bytes_written = profile_mpeg->syntax->flush(profile_mpeg->syntax); else bytes_written = 0; /* Release the decoder */ if(profile_mpeg->decoder && profile_mpeg->decoder->close) profile_mpeg->decoder->close(profile_mpeg->decoder); /* Release the encoder */ if(profile_mpeg->encoder && profile_mpeg->encoder->close) profile_mpeg->encoder->close(profile_mpeg->encoder); /* Release the syntax */ if(profile_mpeg->syntax && profile_mpeg->syntax->close) profile_mpeg->syntax->close(profile_mpeg->syntax); /* Release the motion estimation */ if(profile_mpeg->motion && profile_mpeg->motion->close) profile_mpeg->motion->close(profile_mpeg->motion); /* Release the shape coder */ if(profile_mpeg->shape && profile_mpeg->shape->close) profile_mpeg->shape->close(profile_mpeg->shape); /* Release the rate controller */ if(profile_mpeg->rate && profile_mpeg->rate->close) profile_mpeg->rate->close(profile_mpeg->rate); /* Release statistics monitoring */ if(profile_mpeg->monitor && profile_mpeg->monitor->close) profile_mpeg->monitor->close(profile_mpeg->monitor); /* Free reference shape */ if(profile_mpeg->ref_shape) fame_free(profile_mpeg->ref_shape); /* Free BAB map */ if(profile_mpeg->bab_map) fame_free(profile_mpeg->bab_map); /* Free reference ring */ for(j = 0; j < 2; j++) /* 2 references */ for(i = 0; i < 4; i++) { /* 4 planes per reference (interpolation) */ /* remove offset */ profile_mpeg->ref[j][i]->y -= 16*(profile_mpeg->width+32)+16; fame_free(profile_mpeg->ref[j][i]->y); } /* Print newline for picture codes */ if(profile_mpeg->verbose) FAME_INFO("\n"); /* Return the number of bytes written to buffer */ return(bytes_written); }