def get_pipeline_score(enc_in_format, csc_spec, encoder_spec, width, height, scaling, target_quality, min_quality, target_speed, min_speed, current_csce, current_ve, client_score_delta): """ Given an optional csc step (csc_format and csc_spec), and and a required encoding step (encoder_spec and width/height), we calculate a score of how well this matches our requirements: * our quality target "self._currend_quality" * our speed target "self._current_speed" * how expensive it would be to switch to this pipeline option Note: we know the current pipeline settings, so the "switching cost" will be lower for pipelines that share components with the current one. Can be called from any thread. """ def clamp(v): return max(0, min(100, v)) qscore = clamp( get_quality_score(enc_in_format, csc_spec, encoder_spec, scaling, target_quality, min_quality)) sscore = clamp( get_speed_score(enc_in_format, csc_spec, encoder_spec, scaling, target_speed, min_speed)) #how well the codec deals with larger screen sizes: sizescore = 100 mpixels = width * height / (1024.0 * 1024.0) if mpixels > 1.0: #high size efficiency means sizescore stays high even with high number of mpixels, #ie: 1MPixels -> sizescore = 100 #ie: 8MPixels -> sizescore = size_efficiency sdisc = (100 - encoder_spec.size_efficiency) sizescore = max(0, 100 - (mpixels - 1) / 7.0 * sdisc) #runtime codec adjustements: runtime_score = 100 #score for "edge resistance" via setup cost: ecsc_score = 100 csc_width = 0 csc_height = 0 if csc_spec: #OR the masks so we have a chance of making it work width_mask = csc_spec.width_mask & encoder_spec.width_mask height_mask = csc_spec.height_mask & encoder_spec.height_mask csc_width = width & width_mask csc_height = height & height_mask if enc_in_format == "RGB": #converting to "RGB" is often a waste of CPU #(can only get selected because the csc step will do scaling, # but even then, the YUV subsampling are better options) ecsc_score = 1 elif current_csce is None or current_csce.get_dst_format()!=enc_in_format or \ type(current_csce)!=csc_spec.codec_class or \ current_csce.get_src_width()!=csc_width or current_csce.get_src_height()!=csc_height: #if we have to change csc, account for new csc setup cost: ecsc_score = max(0, 80 - csc_spec.setup_cost * 80.0 / 100.0) else: ecsc_score = 80 ecsc_score += csc_spec.score_boost runtime_score *= csc_spec.get_runtime_factor() csc_scaling = scaling encoder_scaling = (1, 1) if scaling != (1, 1) and not csc_spec.can_scale: #csc cannot take care of scaling, so encoder will have to: encoder_scaling = scaling csc_scaling = (1, 1) if scaling != (1, 1): #if we are (down)scaling, we should prefer lossy pixel formats: v = LOSSY_PIXEL_FORMATS.get(enc_in_format, 1) qscore *= (v / 2) enc_width, enc_height = get_encoder_dimensions(encoder_spec, csc_width, csc_height, scaling) else: #not using csc at all! ecsc_score = 100 width_mask = encoder_spec.width_mask height_mask = encoder_spec.height_mask enc_width = width & width_mask enc_height = height & height_mask csc_scaling = None encoder_scaling = scaling if encoder_scaling != (1, 1) and not encoder_spec.can_scale: #we need the encoder to scale but it cannot do it, fail it: scorelog("scaling (%s) not supported by %s", encoder_scaling, encoder_spec) return None if enc_width < encoder_spec.min_w or enc_height < encoder_spec.min_h: scorelog("video size %ix%i out of range for %s, min %ix%i", enc_width, enc_height, encoder_spec.codec_type, encoder_spec.min_w, encoder_spec.min_h) return None elif enc_width > encoder_spec.max_w or enc_height > encoder_spec.max_h: scorelog("video size %ix%i out of range for %s, max %ix%i", enc_width, enc_height, encoder_spec.codec_type, encoder_spec.max_w, encoder_spec.max_h) return None ee_score = 100 if current_ve is None or current_ve.get_type()!=encoder_spec.codec_type or \ current_ve.get_src_format()!=enc_in_format or \ current_ve.get_width()!=enc_width or current_ve.get_height()!=enc_height: #account for new encoder setup cost: ee_score = 100 - encoder_spec.setup_cost ee_score += encoder_spec.score_boost #edge resistance score: average of csc and encoder score: er_score = (ecsc_score + ee_score) / 2.0 score = int((qscore + sscore + er_score + sizescore + client_score_delta) * runtime_score / 100.0 / 4.0) scorelog( "get_score(%-7s, %-24r, %-24r, %5i, %5i) quality: %2i, speed: %2i, setup: %2i - %2i runtime: %2i scaling: %s / %s, encoder dimensions=%sx%s, sizescore=%3i, client score delta=%3i, score=%2i", enc_in_format, csc_spec, encoder_spec, width, height, qscore, sscore, ecsc_score, ee_score, runtime_score, scaling, encoder_scaling, enc_width, enc_height, sizescore, client_score_delta, score) return score, scaling, csc_scaling, csc_width, csc_height, csc_spec, enc_in_format, encoder_scaling, enc_width, enc_height, encoder_spec
def get_pipeline_score(enc_in_format, csc_spec, encoder_spec, width, height, scaling, target_quality, min_quality, target_speed, min_speed, current_csce, current_ve, client_score_delta): """ Given an optional csc step (csc_format and csc_spec), and and a required encoding step (encoder_spec and width/height), we calculate a score of how well this matches our requirements: * our quality target "self._currend_quality" * our speed target "self._current_speed" * how expensive it would be to switch to this pipeline option Note: we know the current pipeline settings, so the "switching cost" will be lower for pipelines that share components with the current one. Can be called from any thread. """ def clamp(v): return max(0, min(100, v)) qscore = clamp(get_quality_score(enc_in_format, csc_spec, encoder_spec, scaling, target_quality, min_quality)) sscore = clamp(get_speed_score(enc_in_format, csc_spec, encoder_spec, scaling, target_speed, min_speed)) #how well the codec deals with larger screen sizes: sizescore = 100 mpixels = width*height/(1024.0*1024.0) if mpixels>1.0: #high size efficiency means sizescore stays high even with high number of mpixels, #ie: 1MPixels -> sizescore = 100 #ie: 8MPixels -> sizescore = size_efficiency sdisc = (100-encoder_spec.size_efficiency) sizescore = max(0, 100-(mpixels-1)/7.0*sdisc) #runtime codec adjustements: runtime_score = 100 #score for "edge resistance" via setup cost: ecsc_score = 100 csc_width = 0 csc_height = 0 if csc_spec: #OR the masks so we have a chance of making it work width_mask = csc_spec.width_mask & encoder_spec.width_mask height_mask = csc_spec.height_mask & encoder_spec.height_mask csc_width = width & width_mask csc_height = height & height_mask if enc_in_format=="RGB": #converting to "RGB" is often a waste of CPU #(can only get selected because the csc step will do scaling, # but even then, the YUV subsampling are better options) ecsc_score = 1 elif current_csce is None or current_csce.get_dst_format()!=enc_in_format or \ type(current_csce)!=csc_spec.codec_class or \ current_csce.get_src_width()!=csc_width or current_csce.get_src_height()!=csc_height: #if we have to change csc, account for new csc setup cost: ecsc_score = max(0, 80 - csc_spec.setup_cost*80.0/100.0) else: ecsc_score = 80 ecsc_score += csc_spec.score_boost runtime_score *= csc_spec.get_runtime_factor() csc_scaling = scaling encoder_scaling = (1, 1) if scaling!=(1,1) and not csc_spec.can_scale: #csc cannot take care of scaling, so encoder will have to: encoder_scaling = scaling csc_scaling = (1, 1) if scaling!=(1, 1): #if we are (down)scaling, we should prefer lossy pixel formats: v = LOSSY_PIXEL_FORMATS.get(enc_in_format, 1) qscore *= (v/2) enc_width, enc_height = get_encoder_dimensions(csc_spec, encoder_spec, csc_width, csc_height, scaling) else: #not using csc at all! ecsc_score = 100 width_mask = encoder_spec.width_mask height_mask = encoder_spec.height_mask enc_width = width & width_mask enc_height = height & height_mask csc_scaling = None encoder_scaling = scaling if encoder_scaling!=(1,1) and not encoder_spec.can_scale: #we need the encoder to scale but it cannot do it, fail it: scorelog("scaling (%s) not supported by %s", encoder_scaling, encoder_spec) return None ee_score = 100 if current_ve is None or current_ve.get_type()!=encoder_spec.codec_type or \ current_ve.get_src_format()!=enc_in_format or \ current_ve.get_width()!=enc_width or current_ve.get_height()!=enc_height: #account for new encoder setup cost: ee_score = 100 - encoder_spec.setup_cost ee_score += encoder_spec.score_boost #edge resistance score: average of csc and encoder score: er_score = (ecsc_score + ee_score) / 2.0 score = int((qscore+sscore+er_score+sizescore+client_score_delta)*runtime_score/100.0/4.0) scorelog("get_score(%-7s, %-24r, %-24r, %5i, %5i) quality: %2i, speed: %2i, setup: %2i runtime: %2i scaling: %s / %s, encoder dimensions=%sx%s, sizescore=%3i, client score delta=%3i, score=%2i", enc_in_format, csc_spec, encoder_spec, width, height, qscore, sscore, er_score, runtime_score, scaling, encoder_scaling, enc_width, enc_height, sizescore, client_score_delta, score) return score, scaling, csc_scaling, csc_width, csc_height, csc_spec, enc_in_format, encoder_scaling, enc_width, enc_height, encoder_spec