| #!/usr/bin/env python3 | |
| ## Copyright (c) 2023, Alliance for Open Media. All rights reserved | |
| ## | |
| ## This source code is subject to the terms of the BSD 3-Clause Clear License and the | |
| ## Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear License was | |
| ## not distributed with this source code in the LICENSE file, you can obtain it | |
| ## at aomedia.org/license/software-license/bsd-3-c-c/. If the Alliance for Open Media Patent | |
| ## License 1.0 was not distributed with this source code in the PATENTS file, you | |
| ## can obtain it at aomedia.org/license/patent-license/. | |
| ## | |
| from __future__ import annotations | |
| import abc | |
| from absl import logging | |
| from collections.abc import Iterable, Iterator | |
| from typing import Generic, TypeVar | |
| from avm_stats import proto_helpers | |
| import pandas as pd | |
| # Type of the object we're extracting data from. This will typically be | |
| # superblocks, coding units, or transform units. | |
| _TObj = TypeVar("_TObj") | |
| # Type of the data that gets extracted from the objects we iterate over. Typically this will be a namedtuple or similar type that can easily be converted into a pandas dataframe. | |
| _TData = TypeVar("_TData") | |
| class Extractor(Generic[_TObj, _TData], metaclass=abc.ABCMeta): | |
| """Takes in a single frame, and extracts some data of interest from subobjects contained within that frame.""" | |
| @abc.abstractmethod | |
| def sample(self, obj: _TObj) -> Iterator[_TData]: | |
| pass | |
| def extract(self, frame: proto_helpers.Frame) -> Iterator[_TData]: | |
| for obj in self.get_objects(frame): | |
| yield from self.sample(obj) | |
| def extract_to_dataframe(self, frame: proto_helpers.Frame) -> pd.DataFrame: | |
| return pd.DataFrame(self.extract(frame)) | |
| @abc.abstractmethod | |
| def get_objects(self, frame: proto_helpers.Frame) -> Iterator[_TObj]: | |
| """Typically this will either be superblocks, coding units, or transform units.""" | |
| pass | |
| class FrameExtractor(Extractor): | |
| def get_objects(self, frame: proto_helpers.Frame) -> Iterator[_TObj]: | |
| yield frame | |
| class SuperblockExtractor(Extractor): | |
| def get_objects(self, frame: proto_helpers.Frame) -> Iterator[_TObj]: | |
| yield from frame.superblocks | |
| class CodingUnitExtractor(Extractor): | |
| def get_objects(self, frame: proto_helpers.Frame) -> Iterator[_TObj]: | |
| # TODO(comc): Add an option to get chroma CUs as well. | |
| for sb in frame.superblocks: | |
| yield from sb.get_coding_units() | |
| def aggregate_frames( | |
| frames: Iterable[proto_helpers.Frame], extractor: Extractor | |
| ) -> Iterator[_TData]: | |
| for frame in frames: | |
| logging.info(f"Processing {frame.proto.stream_params.stream_name} - frame {frame.proto.frame_params.decode_index}") | |
| yield from extractor.extract(frame) | |
| def aggregate_to_dataframe( | |
| frames: Iterable[proto_helpers.Frame], extractor: Extractor | |
| ) -> pd.DataFrame: | |
| return pd.DataFrame(aggregate_frames(frames, extractor)) |