def make_observation(
        self,
        test_type,
        mezzanine_qr_codes: List[MezzanineDecodedQr],
        test_status_qr_codes: List[TestStatusDecodedQr],
        parameters_dict: dict,
        time_diff_file: str,
    ) -> Dict[str, str]:
        """Implements the logic:
        sample_tolerance_in_recording = ct_frame_tolerance * 1000/mezzanine_frame_rate/(1000/camera_frame_rate)
            = ct_frame_tolerance * camera_frame_rate/mezzanine_frame_rate
        sample_tolerance = ct_frame_tolerance * 1000/mezzanine_frame_rate

        target_camera_frame_num_of_ct_event
            = ct_event.first_seen_camera_frame_num - (ct_event.d / camera_frame_duration_ms)
        first_possible_camera_frame_num_of_target
            = target_camera_frame_num_of_ct_event - CAMERA_FRAME_ADJUSTMENT - sample_tolerance_in_recording
        last_possible_camera_frame_num_of_target
            = target_camera_frame_num_of_ct_event + CAMERA_FRAME_ADJUSTMENT  + sample_tolerance_in_recording

        for first_possible_camera_frame_num_of_target to last_possible_camera_frame_num_of_target
                foreach mezzanine_qr_code on camera_frame that within the range
                        if mezzanine_qr_code.media_time == (ct_event.current_time +/- (sample_tolerance + tolerance))
                                test is PASSED
        """
        logger.info(f"Making observation {self.result['name']}...")

        if not mezzanine_qr_codes:
            self.result["status"] = "FAIL"
            self.result["message"] = f"No QR mezzanine code detected."
            logger.info(f"[{self.result['status']}] {self.result['message']}")
            return self.result

        camera_frame_rate = parameters_dict["camera_frame_rate"]
        camera_frame_duration_ms = parameters_dict["camera_frame_duration_ms"]
        allowed_tolerance = parameters_dict["tolerance"]
        ct_frame_tolerance = parameters_dict["frame_tolerance"]
        failure_report_count = 0
        self.result[
            "message"] += f" Allowed tolerance is {ct_frame_tolerance} frames, {allowed_tolerance}ms."

        # for splicing test adjust media time in mezzanine_qr_codes
        # media time for period 2 starts from 0 so the actual media time is += period_duration[0]
        # media time for period 3 starts from where it was left but need to add the ad insertion duration
        # so the actual media time is += period_duration[1]
        if test_type == TestType.SPLICING:
            period_list = PlayoutParser.get_splicing_period_list(
                parameters_dict["playout"],
                parameters_dict["fragment_duration_multi_mpd"])
            change_type_list = PlayoutParser.get_change_type_list(
                parameters_dict["playout"])

            period_index = 0
            change_count = 0
            current_content_id = mezzanine_qr_codes[0].content_id
            current_frame_rate = mezzanine_qr_codes[0].frame_rate
            for i in range(1, len(mezzanine_qr_codes)):
                if (mezzanine_qr_codes[i].content_id != current_content_id
                        or mezzanine_qr_codes[i].frame_rate !=
                        current_frame_rate):
                    # the content did change
                    change_count += 1
                    current_content_id = mezzanine_qr_codes[i].content_id
                    current_frame_rate = mezzanine_qr_codes[i].frame_rate

                    if change_type_list[change_count - 1] == "splicing":
                        period_index += 1

                if period_index > 0:
                    mezzanine_qr_codes[i].media_time += period_list[
                        period_index - 1]

        time_differences = []

        for i in range(0, len(test_status_qr_codes)):
            current_status = test_status_qr_codes[i]
            if i + 1 < len(test_status_qr_codes):
                if (current_status.status == "playing"
                        and current_status.last_action == "play"):
                    first_possible, last_possible = self._get_target_camera_frame_num(
                        current_status.camera_frame_num,
                        test_status_qr_codes[i + 1].delay,
                        camera_frame_duration_ms, camera_frame_rate,
                        mezzanine_qr_codes, ct_frame_tolerance)
                    diff_found, time_diff = self._find_diff_within_tolerance(
                        mezzanine_qr_codes, current_status, first_possible,
                        last_possible, allowed_tolerance, ct_frame_tolerance)
                    # The multiplication happens so that we get the results in ms
                    time_differences.append(
                        (current_status.current_time * 1000, time_diff))

                    if not diff_found:
                        self.result["status"] = "FAIL"
                        if failure_report_count == 0:
                            self.result["message"] += (
                                " Time difference between Test Runner reported media currentTime and actual media "
                                "time exceeded tolerance for following events:"
                            )

                        if failure_report_count < REPORT_NUM_OF_FAILURE:
                            self.result[
                                "message"] += f" currentTime={current_status.current_time} time_diff={round(time_diff, 4)}; "

                        failure_report_count += 1

        if failure_report_count >= REPORT_NUM_OF_FAILURE:
            self.result[
                "message"] += f"...too many failures, reporting truncated. Total failure count is {failure_report_count}. "

        if self.result["status"] != "FAIL":
            self.result["status"] = "PASS"

        logger.debug(f"[{self.result['status']}]: {self.result['message']}")

        # Exporting time diff data to a CSV file
        if time_diff_file and time_differences:
            self._write_time_differences(time_diff_file, time_differences)

        return self.result
コード例 #2
0
    def observe_splicing_mid_frame(
            self, mezzanine_qr_codes: List[MezzanineDecodedQr],
            playouts: List[List[int]],
            fragment_duration_multi_mpd: dict) -> bool:
        """playout[i]: Provides the triple (Switching Set, CMAF track number, Fragment number) 
        for every playout position i=1,…,N that is be played out.

        on each splicing point:
            check previous ending and new starting frames are correct for each periods
            check that the samples shall be rendered in increasing order within the same period
            for QRb to QRn: QR[i-1].mezzanine_frame_num + 1 == QR[i].mezzanine_frame_num
        """
        splice_start_frame_num_tolerance = self.tolerances[
            "splice_start_frame_num_tolerance"]
        splice_end_frame_num_tolerance = self.tolerances[
            "splice_end_frame_num_tolerance"]

        change_starting_index_list = self.get_content_change_position(
            mezzanine_qr_codes)
        change_type_list = PlayoutParser.get_change_type_list(playouts)
        ending_playout_list = PlayoutParser.get_ending_playout_list(playouts)
        starting_playout_list = PlayoutParser.get_starting_playout_list(
            playouts)

        # check if the configured content change and actual content change matches
        # if not report error
        actual_change_num = len(change_starting_index_list)
        configured_change_num = len(change_type_list) + 1
        if actual_change_num != configured_change_num:
            self.result["message"] += (
                f" Number of changes does not match the 'playout' configuration. "
                f"Test is configured to change {configured_change_num} times. "
                f"Actual number of change is {actual_change_num}. ")
            return False
        # check mid frames block by block based on the starting index of content change
        mid_frame_result = True
        for i, starting_index in enumerate(change_starting_index_list):
            if starting_index == change_starting_index_list[-1]:
                # check mid frames for last block
                check_frame_result = mid_frame_result and self._check_every_frame(
                    mezzanine_qr_codes[change_starting_index_list[-1]:])
                mid_frame_result = mid_frame_result and check_frame_result
            else:
                last_index = change_starting_index_list[i + 1] - 1
                check_frame_result = mid_frame_result and self._check_every_frame(
                    mezzanine_qr_codes[starting_index:last_index])
                mid_frame_result = mid_frame_result and check_frame_result

            if i > 0:
                # check previous ending frame and new starting frame numbers
                ending_playout = ending_playout_list[i - 1]
                ending_fragment_duration = fragment_duration_multi_mpd[(
                    ending_playout[0], ending_playout[1])]
                ending_fragment_num = ending_playout[2]
                previous_ending_frame_num = round(
                    ending_fragment_num * ending_fragment_duration / 1000 *
                    mezzanine_qr_codes[starting_index - 1].frame_rate)

                # compare expected with the actual frame number detected at this splice point
                diff_ending_frame = abs(mezzanine_qr_codes[starting_index -
                                                           1].frame_number -
                                        previous_ending_frame_num)

                if change_type_list[i - 1] == "splicing":
                    if diff_ending_frame > splice_end_frame_num_tolerance:
                        mid_frame_result = False
                        self.result["message"] += (
                            f" Ending with incorrect frame when splicing at period number {i}. "
                            f"Ending frame found is {mezzanine_qr_codes[starting_index -1].frame_number }, "
                            f"expected to end with {previous_ending_frame_num}. "
                            f"Splice end frame tolerance is {splice_end_frame_num_tolerance}."
                        )
                else:
                    if diff_ending_frame > 0:
                        mid_frame_result = False
                        self.result["message"] += (
                            f" Ending with incorrect frame when switching at number {i}. "
                            f"Ending frame found is {mezzanine_qr_codes[starting_index -1].frame_number }, "
                            f"expected to end with {previous_ending_frame_num}. "
                        )

                starting_playout = starting_playout_list[i - 1]
                starting_fragment_duration = fragment_duration_multi_mpd[(
                    starting_playout[0], starting_playout[1])]
                starting_fragment_num = starting_playout[2] - 1
                current_starting_frame_num = (round(
                    starting_fragment_num * starting_fragment_duration / 1000 *
                    mezzanine_qr_codes[starting_index].frame_rate) + 1)

                # compare expected with the actual frame number detected at this splice point
                diff_starting_frame = abs(
                    mezzanine_qr_codes[starting_index].frame_number -
                    current_starting_frame_num)

                if change_type_list[i - 1] == "splicing":
                    if diff_starting_frame > splice_start_frame_num_tolerance:
                        mid_frame_result = False
                        self.result["message"] += (
                            f" Starting from incorrect frame when splicing at period number {i + 1}. "
                            f"Starting frame found is {mezzanine_qr_codes[starting_index].frame_number }, "
                            f"expected to start from {current_starting_frame_num}. "
                            f"Splice start frame tolerance is {splice_start_frame_num_tolerance}."
                        )
                else:
                    if diff_starting_frame > 0:
                        mid_frame_result = False
                        self.result["message"] += (
                            f" Starting from incorrect frame when switching at number {i + 1}. "
                            f"Starting frame found is {mezzanine_qr_codes[starting_index].frame_number }, "
                            f"expected to start from {current_starting_frame_num}. "
                        )

        return mid_frame_result
コード例 #3
0
    def make_observation(
        self,
        test_type,
        mezzanine_qr_codes: List[MezzanineDecodedQr],
        _unused,
        parameters_dict: dict,
        _unused2,
    ) -> Dict[str, str]:
        """
        make_observation for different test type

        check 1st frame is present
        QRa.mezzanine_frame_num == first_frame_num

        check the last frame is present
        QRn.mezzanine_frame_num == round(cmaf_track_duration * mezzanine_frame_rate)

        Args:
            test_type: SWITCHING|SPLICING|SEQUENTIAL
            mezzanine_qr_codes: lists of MezzanineDecodedQr
            _unused:
            parameters_dict: parameter dictionary
            _unused2

        Returns:
            Dict[str, str]: observation result
        """
        logger.info(f"Making observation {self.result['name']}...")

        if len(mezzanine_qr_codes) < 2:
            self.result["status"] = "FAIL"
            self.result[
                "message"] = f"Too few mezzanine QR codes detected ({len(mezzanine_qr_codes)})."
            logger.info(f"[{self.result['status']}] {self.result['message']}")
            return self.result

        first_frame_result = self._check_first_frame(
            parameters_dict["first_frame_num"], mezzanine_qr_codes[0])
        last_frame_result = self._check_last_frame(
            parameters_dict["last_frame_num"], mezzanine_qr_codes[-1])

        if test_type == TestType.SWITCHING:
            switching_playout = PlayoutParser.get_switching_playout(
                parameters_dict["playout"])
            mid_frame_result = self.observe_switching_mid_frame(
                mezzanine_qr_codes,
                switching_playout,
                parameters_dict["fragment_duration_list"],
            )
        elif test_type == TestType.SPLICING:
            mid_frame_result = self.observe_splicing_mid_frame(
                mezzanine_qr_codes,
                parameters_dict["playout"],
                parameters_dict["fragment_duration_multi_mpd"],
            )
        else:
            # check that the samples shall be rendered in increasing order:
            # for QRb to QRn: QR[i-1].mezzanine_frame_num + 1 == QR[i].mezzanine_frame_num
            mid_frame_result = self._check_every_frame(mezzanine_qr_codes)

        self.result["message"] += (
            f" Total of missing frames is {self.missing_frame_count}.")

        if first_frame_result and last_frame_result and mid_frame_result:
            self.result["status"] = "PASS"
        else:
            self.result["status"] = "FAIL"

        logger.debug(f"[{self.result['status']}]: {self.result['message']}")
        return self.result
コード例 #4
0
    def observe_switching_mid_frame(
        self,
        mezzanine_qr_codes: List[MezzanineDecodedQr],
        playout: List[int],
        fragment_duration_list: Dict[int, float],
    ) -> bool:
        """observe switching set tests
        playback more than one representations

        Parse playout parameter to a list of switching point in media timeline
        switching_positions: a list of switching position in media timeline

        check every switching points starting frame and ending frame
        check that the samples shall be rendered in increasing order within the same representations
        for QRb to QRn: QR[i-1].mezzanine_frame_num + 1 == QR[i].mezzanine_frame_num
        """
        switching_positions = []
        switching_position = 0
        switching_positions.append(switching_position)
        for i in range(1, len(playout)):
            switching_position += fragment_duration_list[playout[i]]
            # when track change
            if playout[i] != playout[i - 1]:
                switching_positions.append(switching_position)

        change_switching_index_list = self.get_content_change_position(
            mezzanine_qr_codes)

        # check configuration and actual switching matches
        configured_switching_num = len(switching_positions)
        actual_switching_num = len(change_switching_index_list)
        if actual_switching_num != configured_switching_num:
            self.result["message"] += (
                f" Number of switches does not match. "
                f"Test is configured to switch {configured_switching_num} times. "
                f"Actual number of switches is {actual_switching_num}. ")
            return False

        # check mid frames block by block
        playout_sequence = PlayoutParser.get_playout_sequence(playout)
        mid_frame_result = self.check_every_frame_by_block(
            mezzanine_qr_codes, change_switching_index_list, playout_sequence)
        for i, starting_index in enumerate(change_switching_index_list):
            if i > 0:
                # check previous ending frame and new starting frame numbers
                # the expected frame number position in the content being switched from is the expected relative time
                # of the switch (derived from the test config) * that content's frames per second
                previous_ending_frame_num = round(
                    switching_positions[i] / 1000 *
                    mezzanine_qr_codes[starting_index - 1].frame_rate)
                # compare expected with the actual frame number detected at this switch point
                diff_ending_frame = abs(mezzanine_qr_codes[starting_index -
                                                           1].frame_number -
                                        previous_ending_frame_num)
                if diff_ending_frame != 0:
                    mid_frame_result = False
                    self.result["message"] += (
                        f" Playout {playout_sequence[i - 1]} ending frame found is {mezzanine_qr_codes[starting_index - 1].frame_number },"
                        f" expected to end with {previous_ending_frame_num}.")

                # the expected frame number position in the content being switched to is the expected relative time
                # of the switch (derived from the test config) * that content's frames per second
                # compare expected with the actual frame number detected at this switch point
                current_starting_frame_num = (
                    round(switching_positions[i] / 1000 *
                          mezzanine_qr_codes[starting_index].frame_rate) + 1)
                diff_starting_frame = abs(
                    mezzanine_qr_codes[starting_index].frame_number -
                    current_starting_frame_num)
                if diff_starting_frame != 0:
                    mid_frame_result = False
                    self.result["message"] += (
                        f" Playout {playout_sequence[i]} starting frame found is {mezzanine_qr_codes[starting_index].frame_number },"
                        f" expected to start from {current_starting_frame_num}."
                    )

        return mid_frame_result