On Tue, Feb 26, 2013 at 01:03:52PM -0500, Yonit Halperin wrote: > Downgrading stream bit rate when the input frame rate in the server > exceeds the output frame rate, and frames are being dropped from the > output pipe. ACK. > --- > server/mjpeg_encoder.c | 103 ++++++++++++++++++++++++++++++++++++++----------- > server/mjpeg_encoder.h | 10 +++++ > 2 files changed, 91 insertions(+), 22 deletions(-) > > diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c > index 6e80929..d50a5d1 100644 > --- a/server/mjpeg_encoder.c > +++ b/server/mjpeg_encoder.c > @@ -40,6 +40,9 @@ static const int mjpeg_quality_samples[MJPEG_QUALITY_SAMPLE_NUM] = {20, 30, 40, > #define MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES 3 > #define MJPEG_LOW_FPS_RATE_TH 3 > > +#define MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL 1 > +#define MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH 0.1 > + > /* > * acting on positive client reports only if enough frame mm time > * has passed since the last bit rate change and the report. > @@ -80,6 +83,11 @@ typedef struct MJpegEncoderClientState { > uint32_t max_audio_latency; > } MJpegEncoderClientState; > > +typedef struct MJpegEncoderServerState { > + uint32_t num_frames_encoded; > + uint32_t num_frames_dropped; > +} MJpegEncoderServerState; > + > typedef struct MJpegEncoderBitRateInfo { > uint64_t change_start_time; > uint64_t last_frame_time; > @@ -106,6 +114,7 @@ typedef struct MJpegEncoderRateControl { > MJpegEncoderQualityEval quality_eval_data; > MJpegEncoderBitRateInfo bit_rate_info; > MJpegEncoderClientState client_state; > + MJpegEncoderServerState server_state; > > uint64_t byte_rate; > int quality_id; > @@ -141,6 +150,7 @@ static inline void mjpeg_encoder_reset_quality(MJpegEncoder *encoder, > uint32_t fps, > uint64_t frame_enc_size); > static uint32_t get_max_fps(uint64_t frame_size, uint64_t bytes_per_sec, uint32_t latency_ms); > +static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder); > > MJpegEncoder *mjpeg_encoder_new(int bit_rate_control, uint64_t starting_bit_rate, > MJpegEncoderRateControlCbs *cbs, void *opaque) > @@ -343,6 +353,10 @@ static inline void mjpeg_encoder_reset_quality(MJpegEncoder *encoder, > rate_control->last_enc_size = 0; > } > > + > + if (rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) { > + memset(&rate_control->server_state, 0, sizeof(MJpegEncoderServerState)); > + } > rate_control->quality_id = quality_id; > memset(&rate_control->quality_eval_data, 0, sizeof(MJpegEncoderQualityEval)); > rate_control->quality_eval_data.max_quality_id = MJPEG_QUALITY_SAMPLE_NUM - 1; > @@ -523,7 +537,7 @@ static void mjpeg_encoder_adjust_params_to_bit_rate(MJpegEncoder *encoder) > { > MJpegEncoderRateControl *rate_control; > MJpegEncoderQualityEval *quality_eval; > - uint64_t new_avg_enc_size; > + uint64_t new_avg_enc_size = 0; > uint32_t new_fps; > uint32_t latency = 0; > uint32_t src_fps; > @@ -550,7 +564,7 @@ static void mjpeg_encoder_adjust_params_to_bit_rate(MJpegEncoder *encoder) > > if (rate_control->num_recent_enc_frames < MJPEG_AVERAGE_SIZE_WINDOW && > rate_control->num_recent_enc_frames < rate_control->fps) { > - return; > + goto end; > } > > latency = mjpeg_encoder_get_latency(encoder); > @@ -591,10 +605,12 @@ static void mjpeg_encoder_adjust_params_to_bit_rate(MJpegEncoder *encoder) > rate_control->quality_id, > rate_control->fps); > } > - > +end: > if (rate_control->during_quality_eval) { > quality_eval->encoded_size_by_quality[rate_control->quality_id] = new_avg_enc_size; > mjpeg_encoder_eval_quality(encoder); > + } else { > + mjpeg_encoder_process_server_drops(encoder); > } > } > > @@ -603,10 +619,31 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format, > uint8_t **dest, size_t *dest_len, > uint32_t frame_mm_time) > { > - MJpegEncoderBitRateInfo *bit_rate_info; > uint32_t quality; > > - mjpeg_encoder_adjust_params_to_bit_rate(encoder); > + if (encoder->rate_control_is_active) { > + MJpegEncoderRateControl *rate_control = &encoder->rate_control; > + struct timespec time; > + uint64_t now; > + > + clock_gettime(CLOCK_MONOTONIC, &time); > + now = ((uint64_t) time.tv_sec) * 1000000000 + time.tv_nsec; > + > + mjpeg_encoder_adjust_params_to_bit_rate(encoder); > + > + if (!rate_control->during_quality_eval || > + rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) { > + MJpegEncoderBitRateInfo *bit_rate_info; > + > + bit_rate_info = &encoder->rate_control.bit_rate_info; > + > + if (!bit_rate_info->change_start_time) { > + bit_rate_info->change_start_time = now; > + bit_rate_info->change_start_mm_time = frame_mm_time; > + } > + bit_rate_info->last_frame_time = now; > + } > + } > > encoder->cinfo.in_color_space = JCS_RGB; > encoder->cinfo.input_components = 3; > @@ -654,23 +691,6 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format, > > spice_jpeg_mem_dest(&encoder->cinfo, dest, dest_len); > > - if (!encoder->rate_control.during_quality_eval || > - encoder->rate_control.quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) { > - struct timespec time; > - uint64_t now; > - > - clock_gettime(CLOCK_MONOTONIC, &time); > - now = ((uint64_t) time.tv_sec) * 1000000000 + time.tv_nsec; > - > - bit_rate_info = &encoder->rate_control.bit_rate_info; > - > - if (!bit_rate_info->change_start_time) { > - bit_rate_info->change_start_time = now; > - bit_rate_info->change_start_mm_time = frame_mm_time; > - } > - bit_rate_info->last_frame_time = now; > - } > - > encoder->cinfo.image_width = width; > encoder->cinfo.image_height = height; > jpeg_set_defaults(&encoder->cinfo); > @@ -718,6 +738,7 @@ size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder) > > encoder->first_frame = FALSE; > rate_control->last_enc_size = dest->pub.next_output_byte - dest->buffer; > + rate_control->server_state.num_frames_encoded++; > > if (!rate_control->during_quality_eval || > rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) { > @@ -1059,3 +1080,41 @@ void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder, > } > } > } > + > +void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder) > +{ > + encoder->rate_control.server_state.num_frames_dropped++; > + mjpeg_encoder_process_server_drops(encoder); > +} > + > +/* > + * decrease the bit rate if the drop rate on the sever side exceeds a pre defined > + * threshold. > + */ > +static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder) > +{ > + MJpegEncoderServerState *server_state = &encoder->rate_control.server_state; > + uint32_t num_frames_total; > + double drop_factor; > + uint32_t fps; > + > + fps = MIN(encoder->rate_control.fps, encoder->cbs.get_source_fps(encoder->cbs_opaque)); > + if (server_state->num_frames_encoded < fps * MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL) { > + return; > + } > + > + num_frames_total = server_state->num_frames_dropped + server_state->num_frames_encoded; > + drop_factor = ((double)server_state->num_frames_dropped) / num_frames_total; > + > + spice_debug("#drops %u total %u fps %u src-fps %u", > + server_state->num_frames_dropped, > + num_frames_total, > + encoder->rate_control.fps, > + encoder->cbs.get_source_fps(encoder->cbs_opaque)); > + > + if (drop_factor > MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH) { > + mjpeg_encoder_decrease_bit_rate(encoder); > + } > + server_state->num_frames_encoded = 0; > + server_state->num_frames_dropped = 0; > +} > diff --git a/server/mjpeg_encoder.h b/server/mjpeg_encoder.h > index cc49edf..bc7f01c 100644 > --- a/server/mjpeg_encoder.h > +++ b/server/mjpeg_encoder.h > @@ -84,4 +84,14 @@ void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder, > uint32_t end_frame_mm_time, > int32_t end_frame_delay, > uint32_t audio_delay); > + > +/* > + * Notify the encoder each time a frame is dropped due to pipe > + * congestion. > + * We can deduce the client state by the frame dropping rate in the server. > + * Monitoring the frame drops can help in fine tuning the playback parameters > + * when the client reports are delayed. > + */ > +void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder); > + > #endif > -- > 1.8.1 > > _______________________________________________ > Spice-devel mailing list > Spice-devel@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel