Esempio n. 1
0
class PerformanceMetric:
    def __init__(self):
        self.switches = {"VIDEO": Trace("seconds", "bps"), "AUDIO": Trace("seconds", "bps")}
        self.buffer_levels = {"VIDEO": BufferLevelMetric(), "AUDIO": BufferLevelMetric()}
        self.bps_history = Trace("seconds", "bps")

        self.bps_history.append(0, 0)
        self.buffer_levels["VIDEO"].append(0, 0)
        self.buffer_levels["AUDIO"].append(0, 0)
        self.switches["VIDEO"].append(0, 0)
        self.switches["AUDIO"].append(0, 0)

    @property
    def underrun_count(self):
        return self.buffer_levels["VIDEO"].underrun_count + self.buffer_levels["AUDIO"].underrun_count

    def min_buffer_level(self):
        return min(self.buffer_levels["VIDEO"].current_value, self.buffer_levels["AUDIO"].current_value)

    """ So far, use reciproc of underruns to give a score. 1.0 perfect score """

    def score(self):
        return 1.0 / (self.underrun_count + 1)

    def print_stats(self):
        print("Score: %.4f" % self.score())
        print("Underruns: %d" % self.underrun_count)
        print self.switches["VIDEO"]
Esempio n. 2
0
class PerformanceMetric:
    def __init__(self):
        self.switches = {"VIDEO": Trace("seconds", "bps"), "AUDIO": Trace("seconds", "bps")}
        self.buffer_levels = {"VIDEO": BufferLevelMetric(), "AUDIO": BufferLevelMetric()}
        self.bps_history = Trace("seconds", "bps")

        self.bps_history.append(0, 0)
        self.buffer_levels["VIDEO"].append(0, 0)
        self.buffer_levels["AUDIO"].append(0, 0)
        self.switches["VIDEO"].append(0, 0)
        self.switches["AUDIO"].append(0, 0)

    @property
    def underrun_count(self):
        return self.buffer_levels["VIDEO"].underrun_count + self.buffer_levels["AUDIO"].underrun_count

    def min_buffer_level(self):
        return min(self.buffer_levels["VIDEO"].current_value, self.buffer_levels["AUDIO"].current_value)

    """ So far, use reciproc of underruns to give a score. 1.0 perfect score """

    def score(self):
        return 1.0 / (self.underrun_count + 1)

    def print_stats(self):
        print("Score: %.4f" % self.score())
        print("Underruns: %d" % self.underrun_count)
        print self.switches["VIDEO"]
Esempio n. 3
0
class BufferLevelMetric(Trace):
    def __init__(self):
        Trace.__init__(self, "seconds", "seconds")
        self._level = 0.0 # in seconds
        self._underruns = Trace("seconds", "underrun duration (seconds)")

    @property
    def underrun_count(self):
        return self._underruns.count

    def increase_by(self, absolute_time, level_increase):
        val = self.current_value
        if val is None:
            val = 0.0
        self.append(absolute_time, val + level_increase)

    def decrease_by(self, absolute_time, level_decrease):
        val = self.current_value
        if val is None:
            val = 0.0
        val -= level_decrease
        if val <= 0.0:
            # record an underrun [time, duration_of_underrun]
            self._underruns.append(absolute_time, abs(val))
            # clamp level to 0.0
            val = 0.0
        self.append(absolute_time, val)

    @property
    def level(self):
        """
        Alias for current_value or current_y_value
        :return: current_y_value
        """
        return self.current_value

    def __unicode__(self):
        val = self.current_value
        if val is not None:
            return "BufferLevel(t=%.2fs): %.2fs" % (self.current_x_value, self.current_value)
        else:
            return "BufferLevel(t=0): 0"

    def __str__(self):
        return self.__unicode__()
Esempio n. 4
0
class BufferLevelMetric(Trace):
    def __init__(self):
        Trace.__init__(self, "seconds", "seconds")
        self._level = 0.0 # in seconds
        self._underruns = Trace("seconds", "underrun duration (seconds)")

    @property
    def underrun_count(self):
        return self._underruns.count

    def increase_by(self, absolute_time, level_increase):
        val = self.current_value
        if val is None:
            val = 0.0
        self.append(absolute_time, val + level_increase)

    def decrease_by(self, absolute_time, level_decrease):
        val = self.current_value
        if val is None:
            val = 0.0
        val -= level_decrease
        if val <= 0.0:
            # record an underrun [time, duration_of_underrun]
            self._underruns.append(absolute_time, abs(val))
            # clamp level to 0.0
            val = 0.0
        self.append(absolute_time, val)

    @property
    def level(self):
        """
        Alias for current_value or current_y_value
        :return: current_y_value
        """
        return self.current_value

    def __unicode__(self):
        val = self.current_value
        if val is not None:
            return "BufferLevel(t=%.2fs): %.2fs" % (self.current_x_value, self.current_value)
        else:
            return "BufferLevel(t=0): 0"

    def __str__(self):
        return self.__unicode__()
class CastLabsAdaptation(Adaptation):
    MA_SIZE = 5

    def __init__(self, bitrates):
        Adaptation.__init__(self, bitrates)
        self.max_seconds = 50.0
        self.level_low_seconds = 10.0  # critical buffer level. Fill as fast as possible until level_high_seconds reached if below this value
        self.level_high_seconds = 30.0  # stable buffer level. Try to maintain current bitrate or improve it
        self.bps_history = Trace("time", "bps")
        self.bitrate_selections = {"AUDIO": Trace("time", "Audio-Bitrate selections"),
                                   "VIDEO": Trace("time", "Video-Bitrate selections")}

        self.sim_state = None
        # self.bitrate_selections["VIDEO"].append(-1, 0)
        # self.bitrate_selections["AUDIO"].append(-1, 0)

        self.my_bps = Trace("seconds", "bps")

        self.ma4_filter = alg.IterativeMovingAverage(4)
        self.ma10_filter = alg.IterativeMovingAverage(10)
        self.ma50_filter = alg.IterativeMovingAverage(80)

        self. last_index = 0

    def current_buffer_level(self, type_str):
        return self.simulator.buffer_level(type_str)

    def min_buffer_level(self):
        return self.sim_state.metric.min_buffer_level()

    def clamp(self, val, range):
        return min(max(range[0], val), range[1])

    def next_bitrate(self, type_str):
        index  = 0
        avg = 0
        clamp_range = [0, len(self.bitrates[type_str]) - 1]
        if self.bps_history.length != 0:
            avg = self.ma50_filter(self.bps_history.current_value)
            index = self.clamp(bisect.bisect(self.bitrates[type_str], avg) - 1, clamp_range)
            self.last_index = index
            self.my_bps.append(self.sim_state.t, avg)
        bps = self.bitrates[type_str][index]
        return bps

    def is_buffering(self):
        if self.sim_state is None:
            return False
        else:
            return self.min_buffer_level() < 3.0

    def evaluate(self, next_segment_type, seg_choices, state):
        """
        Updates internal performance statistics
        :param state: current state of the player simulator including buffer levels and http statistics
        :type state: dict of states
        :param next_segment_type: Type of the next segment, "AUDIO" or "VIDEO"
        :type next_segment_type: string
        :return: Next segment that should be downloaded
        """
        self.sim_state = state
        if state.http is not None:
            self.bps_history.append(state.t, state.http.bps)

        bps = self.next_bitrate(next_segment_type)

        if bps != self.bitrate_selections[next_segment_type].current_value:
            if next_segment_type == "VIDEO" and self.my_bps.length > 0:
                print "SWITCH @ t=%.2f: %d -> %d [Avg: %.2f / idx: %d]" % (self.sim_state.t, self.bitrate_selections[next_segment_type].current_value, bps, self.my_bps.current_value, self.last_index)
                print self.bitrates[next_segment_type][self.last_index]
            self.bitrate_selections[next_segment_type].append(state.t, bps)
            print self.bitrate_selections[next_segment_type].x_data
            print self.bitrate_selections[next_segment_type].y_data
        return Segment.find_segment_for_bitrate(seg_choices, bps)