Example #1
0
def update_batch_delay(batch, factors):
    """
        Given a list of factors of the form:
        [(description, factor, weight)]
        we calculate a new batch delay.
        We use a time-weighted average of previous delays as a starting value,
        then combine it with the new factors.
    """
    last_updated = batch.last_updated
    current_delay = batch.delay
    now = time.time()
    avg = 0
    tv, tw = 0.0, 0.0
    decay = max(1, logp(current_delay / batch.min_delay) / 5.0)
    max_delay = batch.max_delay
    for delays in (batch.last_delays, batch.last_actual_delays):
        if len(delays) > 0:
            #get the weighted average
            #older values matter less, we decay them according to how much we batch already
            #(older values matter more when we batch a lot)
            for when, delay in list(delays):
                #newer matter more:
                w = 1.0 / (1.0 + ((now - when) / decay)**2)
                d = max(0, min(max_delay, delay))
                tv += d * w
                tw += w
    if tw > 0:
        avg = tv / tw
    hist_w = tw

    valid_factors = [x for x in factors if x is not None]
    all_factors_weight = sum([w for _, _, w in valid_factors])
    if all_factors_weight == 0:
        log("update_batch_delay: no weights yet!")
        return
    for _, factor, weight in valid_factors:
        target_delay = max(0, min(max_delay, current_delay * factor))
        w = max(1, hist_w) * weight / all_factors_weight
        tw += w
        tv += target_delay * w
    batch.delay = max(0, min(max_delay, tv / tw))
    batch.last_updated = now
    if DEBUG_DELAY > 0:
        decimal_delays = [x for _, x in list(batch.last_delays)]
        if len(decimal_delays) == 0:
            decimal_delays.append(0)
        rec = (
            "update_batch_delay: wid=%s, last updated %.2f ms ago, decay=%.2fs, change factor=%.1f%%, delay min=%i, avg=%i, max=%i, cur=%.1f, w. average=%.1f, tot wgt=%.1f, hist_w=%.1f, new delay=%.1f",
            batch.wid, 1000.0 * now - 1000.0 * last_updated,
            decay, 100.0 * (batch.delay / current_delay - 1),
            min(decimal_delays), sum(decimal_delays) / len(decimal_delays),
            max(decimal_delays), current_delay, avg, tw, hist_w, batch.delay)
        add_DEBUG_MESSAGE(*rec)
        if DEBUG_DELAY > 1:
            logfactors = [("{0:+}".format(int(100.0 * f - 100.0)).rjust(4) +
                           str(int(100 * w)).rjust(8) + "  " + msg)
                          for (msg, f, w) in valid_factors]
            add_DEBUG_MESSAGE("Factors (change - weight - description):\n " +
                              ("\n ".join([str(x) for x in logfactors])))
def update_batch_delay(batch, factors):
    """
        Given a list of factors of the form:
        [(description, factor, weight)]
        we calculate a new batch delay.
        We use a time-weighted average of previous delays as a starting value,
        then combine it with the new factors.
    """
    last_updated = batch.last_updated
    current_delay = batch.delay
    now = time.time()
    avg = 0
    tv, tw = 0.0, 0.0
    decay = max(1, logp(current_delay / batch.min_delay) / 5.0)
    max_delay = batch.max_delay
    for delays in (batch.last_delays, batch.last_actual_delays):
        if len(delays) > 0:
            # get the weighted average
            # older values matter less, we decay them according to how much we batch already
            # (older values matter more when we batch a lot)
            for when, delay in list(delays):
                # newer matter more:
                w = 1.0 / (1.0 + ((now - when) / decay) ** 2)
                d = max(0, min(max_delay, delay))
                tv += d * w
                tw += w
    if tw > 0:
        avg = tv / tw
    hist_w = tw

    valid_factors = [x for x in factors if x is not None]
    all_factors_weight = sum([w for _, _, w in valid_factors])
    if all_factors_weight == 0:
        log("update_batch_delay: no weights yet!")
        return
    for _, factor, weight in valid_factors:
        target_delay = max(0, min(max_delay, current_delay * factor))
        w = max(1, hist_w) * weight / all_factors_weight
        tw += w
        tv += target_delay * w
    batch.delay = max(0, min(max_delay, tv / tw))
    batch.last_updated = now
    if DEBUG_DELAY > 0:
        decimal_delays = [x for _, x in list(batch.last_delays)]
        if len(decimal_delays) == 0:
            decimal_delays.append(0)
        rec = (
            "update_batch_delay: wid=%s, last updated %.2f ms ago, decay=%.2fs, change factor=%.1f%%, delay min=%i, avg=%i, max=%i, cur=%.1f, w. average=%.1f, tot wgt=%.1f, hist_w=%.1f, new delay=%.1f",
            batch.wid,
            1000.0 * now - 1000.0 * last_updated,
            decay,
            100.0 * (batch.delay / current_delay - 1),
            min(decimal_delays),
            sum(decimal_delays) / len(decimal_delays),
            max(decimal_delays),
            current_delay,
            avg,
            tw,
            hist_w,
            batch.delay,
        )
        add_DEBUG_MESSAGE(*rec)
        if DEBUG_DELAY > 1:
            logfactors = [
                ("{0:+}".format(int(100.0 * f - 100.0)).rjust(4) + str(int(100 * w)).rjust(8) + "  " + msg)
                for (msg, f, w) in valid_factors
            ]
            add_DEBUG_MESSAGE(
                "Factors (change - weight - description):\n " + ("\n ".join([str(x) for x in logfactors]))
            )
def update_video_encoder(
    wid,
    window_dimensions,
    batch,
    global_statistics,
    statistics,
    video_encoder=None,
    video_encoder_lock=None,
    video_encoder_speed=None,
    video_encoder_quality=None,
    fixed_quality=-1,
    min_quality=-1,
    fixed_speed=-1,
    min_speed=-1,
):
    low_limit = get_low_limit(global_statistics.mmap_size > 0, window_dimensions)
    # ***********************************************************
    # encoding speed:
    #    0    for highest compression/slower
    #    100  for lowest compression/fast
    # here we try to minimize damage-latency and client decoding speed
    if fixed_speed >= 0:
        new_speed = fixed_speed
        if DEBUG_VIDEO:
            add_DEBUG_MESSAGE("video encoder using fixed speed: %s", fixed_speed)
    else:
        # 20ms + 50ms per MPixel
        min_damage_latency = 0.010 + 0.025 * low_limit / 1024.0 / 1024.0
        target_damage_latency = min_damage_latency + batch.delay / 1000.0
        dam_lat_abs = max(0, ((statistics.avg_damage_in_latency or 0) - min_damage_latency) * 10.0)
        dam_lat_rel = max(0, ((statistics.avg_damage_in_latency or 0) / target_damage_latency) / 2.0)
        target_decode_speed = 8 * 1000 * 1000  # 8 MPixels/s
        dec_lat = 0.0
        if statistics.avg_decode_speed:
            dec_lat = target_decode_speed / (statistics.avg_decode_speed or target_decode_speed)
        target = max(dam_lat_abs, dam_lat_rel, dec_lat, 0.0)
        ms = min(100.0, max(min_speed, 0.0))
        target_speed = ms + (100.0 - ms) * min(1.0, target)
        # make a copy to work on
        ves_copy = list(video_encoder_speed)
        ves_copy.append((time.time(), target_speed))
        new_speed = max(ms, time_weighted_average(ves_copy, min_offset=0.1, rpow=1.2))
        if DEBUG_VIDEO:
            msg = (
                "video encoder speed factors: wid=%s, low_limit=%s, min speed=%s, min_damage_latency=%.3f, avg damage in latency=%.3f, target_damage_latency=%.3f, batch.delay=%.1f, dam_lat=%.3f / %.3f, dec_lat=%.3f, target=%i, new_speed=%i",
                wid,
                low_limit,
                min_speed,
                min_damage_latency,
                statistics.avg_damage_in_latency,
                target_damage_latency,
                batch.delay,
                dam_lat_abs,
                dam_lat_rel,
                dec_lat,
                int(target_speed),
                int(new_speed),
            )
            add_DEBUG_MESSAGE(*msg)

    # ***********************************************************
    # quality:
    #    0    for lowest quality (low bandwidth usage)
    #    100  for best quality (high bandwidth usage)
    # here we try minimize client-latency, packet-backlog and batch.delay
    if fixed_quality >= 0:
        new_quality = fixed_quality
        if DEBUG_VIDEO:
            add_DEBUG_MESSAGE("video encoder using fixed quality: %s", fixed_quality)
    else:
        packets_backlog, _, _ = statistics.get_backlog()
        packets_bl = 1.0 - logp(packets_backlog / low_limit)
        batch_q = batch.min_delay / max(batch.min_delay, batch.delay)
        target = min(packets_bl, batch_q)
        latency_q = 0.0
        if len(global_statistics.client_latency) > 0 and global_statistics.recent_client_latency > 0:
            latency_q = 6.0 * statistics.target_latency / global_statistics.recent_client_latency
            target = min(target, latency_q)
        target = min(1.0, max(0.0, target))
        mq = min(100.0, max(min_quality, 0.0))
        target_quality = mq + (100.0 - mq) * min(1.0, target)
        # make a copy to work on
        veq_copy = list(video_encoder_quality)
        veq_copy.append((time.time(), target_quality))
        new_quality = max(mq, time_weighted_average(veq_copy, min_offset=0.1, rpow=1.1))
        if DEBUG_VIDEO:
            msg = (
                "video encoder quality factors: wid=%s, min quality=%s, packets_bl=%.2f, batch_q=%.2f, latency_q=%.2f, target=%s, new_quality=%s",
                wid,
                min_quality,
                packets_bl,
                batch_q,
                latency_q,
                int(target_quality),
                int(new_quality),
            )
            add_DEBUG_MESSAGE(*msg)

    video_encoder_speed.append((time.time(), new_speed))
    video_encoder_quality.append((time.time(), new_quality))
    try:
        video_encoder_lock.acquire()
        if not video_encoder.is_closed():
            video_encoder.set_encoding_speed(new_speed)
            video_encoder.set_encoding_quality(new_quality)
    finally:
        video_encoder_lock.release()
Example #4
0
def update_video_encoder(wid,
                         window_dimensions,
                         batch,
                         global_statistics,
                         statistics,
                         video_encoder=None,
                         video_encoder_lock=None,
                         video_encoder_speed=None,
                         video_encoder_quality=None,
                         fixed_quality=-1,
                         min_quality=-1,
                         fixed_speed=-1,
                         min_speed=-1):
    low_limit = get_low_limit(global_statistics.mmap_size > 0,
                              window_dimensions)
    #***********************************************************
    # encoding speed:
    #    0    for highest compression/slower
    #    100  for lowest compression/fast
    # here we try to minimize damage-latency and client decoding speed
    if fixed_speed >= 0:
        new_speed = fixed_speed
        if DEBUG_VIDEO:
            add_DEBUG_MESSAGE("video encoder using fixed speed: %s",
                              fixed_speed)
    else:
        #20ms + 50ms per MPixel
        min_damage_latency = 0.010 + 0.025 * low_limit / 1024.0 / 1024.0
        target_damage_latency = min_damage_latency + batch.delay / 1000.0
        dam_lat_abs = max(
            0, ((statistics.avg_damage_in_latency or 0) - min_damage_latency) *
            10.0)
        dam_lat_rel = max(
            0,
            ((statistics.avg_damage_in_latency or 0) / target_damage_latency) /
            2.0)
        target_decode_speed = 8 * 1000 * 1000  #8 MPixels/s
        dec_lat = 0.0
        if statistics.avg_decode_speed:
            dec_lat = target_decode_speed / (statistics.avg_decode_speed
                                             or target_decode_speed)
        target = max(dam_lat_abs, dam_lat_rel, dec_lat, 0.0)
        ms = min(100.0, max(min_speed, 0.0))
        target_speed = ms + (100.0 - ms) * min(1.0, target)
        #make a copy to work on
        ves_copy = list(video_encoder_speed)
        ves_copy.append((time.time(), target_speed))
        new_speed = max(
            ms, time_weighted_average(ves_copy, min_offset=0.1, rpow=1.2))
        if DEBUG_VIDEO:
            msg = "video encoder speed factors: wid=%s, low_limit=%s, min speed=%s, min_damage_latency=%.3f, avg damage in latency=%.3f, target_damage_latency=%.3f, batch.delay=%.1f, dam_lat=%.3f / %.3f, dec_lat=%.3f, target=%i, new_speed=%i", \
                 wid, low_limit, min_speed, min_damage_latency, statistics.avg_damage_in_latency, target_damage_latency, batch.delay, dam_lat_abs, dam_lat_rel, dec_lat, int(target_speed), int(new_speed)
            add_DEBUG_MESSAGE(*msg)

    #***********************************************************
    # quality:
    #    0    for lowest quality (low bandwidth usage)
    #    100  for best quality (high bandwidth usage)
    # here we try minimize client-latency, packet-backlog and batch.delay
    if fixed_quality >= 0:
        new_quality = fixed_quality
        if DEBUG_VIDEO:
            add_DEBUG_MESSAGE("video encoder using fixed quality: %s",
                              fixed_quality)
    else:
        packets_backlog, _, _ = statistics.get_backlog()
        packets_bl = 1.0 - logp(packets_backlog / low_limit)
        batch_q = batch.min_delay / max(batch.min_delay, batch.delay)
        target = min(packets_bl, batch_q)
        latency_q = 0.0
        if len(global_statistics.client_latency
               ) > 0 and global_statistics.recent_client_latency > 0:
            latency_q = 6.0 * statistics.target_latency / global_statistics.recent_client_latency
            target = min(target, latency_q)
        target = min(1.0, max(0.0, target))
        mq = min(100.0, max(min_quality, 0.0))
        target_quality = mq + (100.0 - mq) * min(1.0, target)
        #make a copy to work on
        veq_copy = list(video_encoder_quality)
        veq_copy.append((time.time(), target_quality))
        new_quality = max(
            mq, time_weighted_average(veq_copy, min_offset=0.1, rpow=1.1))
        if DEBUG_VIDEO:
            msg = "video encoder quality factors: wid=%s, min quality=%s, packets_bl=%.2f, batch_q=%.2f, latency_q=%.2f, target=%s, new_quality=%s", \
                 wid, min_quality, packets_bl, batch_q, latency_q, int(target_quality), int(new_quality)
            add_DEBUG_MESSAGE(*msg)

    video_encoder_speed.append((time.time(), new_speed))
    video_encoder_quality.append((time.time(), new_quality))
    try:
        video_encoder_lock.acquire()
        if not video_encoder.is_closed():
            video_encoder.set_encoding_speed(new_speed)
            video_encoder.set_encoding_quality(new_quality)
    finally:
        video_encoder_lock.release()