- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等) - java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等) - javax/ 4个模块 (net/crypto/security/sql) - org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http) - dalvik/ 1个模块 (system) - ~3053个类/接口,方法签名完全对齐Android SDK - 桌面跨平台运行 (Windows/macOS/Linux) - 修复所有导入错误和Python关键字冲突
854 lines
32 KiB
Python
854 lines
32 KiB
Python
"""
|
|
Android Hardware Camera2 Params 组件 — RggbChannelVector, BlackLevelPattern, StreamConfigurationMap 等
|
|
方法签名完全对齐 Android SDK (android.hardware.camera2.params 包)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
from ..java.lang import RuntimeException
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RggbChannelVector
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RggbChannelVector(Stub):
|
|
"""RggbChannelVector — RGGB 通道向量"""
|
|
|
|
COUNT = 4
|
|
|
|
def __init__(self, red: float, green_even: float, green_odd: float, blue: float):
|
|
super().__init__()
|
|
self._red = red
|
|
self._green_even = green_even
|
|
self._green_odd = green_odd
|
|
self._blue = blue
|
|
|
|
def getRed(self) -> float:
|
|
return self._red
|
|
|
|
def getGreenEven(self) -> float:
|
|
return self._green_even
|
|
|
|
def getGreenOdd(self) -> float:
|
|
return self._green_odd
|
|
|
|
def getBlue(self) -> float:
|
|
return self._blue
|
|
|
|
def getComponent(self, i: int) -> float:
|
|
components = [self._red, self._green_even, self._green_odd, self._blue]
|
|
return components[i]
|
|
|
|
def toFloatArray(self) -> List[float]:
|
|
return [self._red, self._green_even, self._green_odd, self._blue]
|
|
|
|
def copyTo(self, destination: List[float], offset: int = 0):
|
|
for i, v in enumerate([self._red, self._green_even, self._green_odd, self._blue]):
|
|
destination[offset + i] = v
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if isinstance(other, RggbChannelVector):
|
|
return (abs(self._red - other._red) < 1e-6 and
|
|
abs(self._green_even - other._green_even) < 1e-6 and
|
|
abs(self._green_odd - other._green_odd) < 1e-6 and
|
|
abs(self._blue - other._blue) < 1e-6)
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._red, self._green_even, self._green_odd, self._blue)) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return f"RggbChannelVector(R={self._red:.3f}, Ge={self._green_even:.3f}, Go={self._green_odd:.3f}, B={self._blue:.3f})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# BlackLevelPattern
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class BlackLevelPattern(Stub):
|
|
"""BlackLevelPattern — 黑电平模式"""
|
|
|
|
COUNT = 4
|
|
|
|
def __init__(self, red: int = 0, green_even: int = 0,
|
|
green_odd: int = 0, blue: int = 0):
|
|
super().__init__()
|
|
self._offsets = [red, green_even, green_odd, blue]
|
|
|
|
def getOffsetForIndex(self, column: int, row: int) -> int:
|
|
idx = (row % 2) * 2 + (column % 2)
|
|
return self._offsets[idx]
|
|
|
|
def copyTo(self, destination: List[int], offset: int = 0):
|
|
for i, v in enumerate(self._offsets):
|
|
destination[offset + i] = v
|
|
|
|
def getRed(self) -> int:
|
|
return self._offsets[0]
|
|
|
|
def getGreenEven(self) -> int:
|
|
return self._offsets[1]
|
|
|
|
def getGreenOdd(self) -> int:
|
|
return self._offsets[2]
|
|
|
|
def getBlue(self) -> int:
|
|
return self._offsets[3]
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if isinstance(other, BlackLevelPattern):
|
|
return self._offsets == other._offsets
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(tuple(self._offsets)) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return f"BlackLevelPattern(R={self._offsets[0]}, Ge={self._offsets[1]}, Go={self._offsets[2]}, B={self._offsets[3]})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# ColorSpaceTransform
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class ColorSpaceTransform(Stub):
|
|
"""ColorSpaceTransform — 色彩空间变换 (3x3 矩阵)"""
|
|
|
|
COUNT = 9
|
|
ELEMENTS_PER_ROW = 3
|
|
ROWS = 3
|
|
COLUMNS = 3
|
|
|
|
def __init__(self, elements: Optional[List[Rational]] = None):
|
|
super().__init__()
|
|
if elements is not None and len(elements) == 9:
|
|
self._elements = list(elements)
|
|
else:
|
|
self._elements = [Rational(1, 1) if i % 4 == 0 else Rational(0, 1) for i in range(9)]
|
|
|
|
def getElement(self, column: int, row: int) -> Rational:
|
|
return self._elements[row * 3 + column]
|
|
|
|
def setElement(self, column: int, row: int, element: Rational):
|
|
self._elements[row * 3 + column] = element
|
|
|
|
def copyTo(self, destination: List[Rational], offset: int = 0):
|
|
for i, v in enumerate(self._elements):
|
|
destination[offset + i] = v
|
|
|
|
def getElements(self) -> List[Rational]:
|
|
return list(self._elements)
|
|
|
|
def transform(self, r: float, g: float, b: float) -> Tuple[float, float, float]:
|
|
e = self._elements
|
|
r_out = e[0].toFloat() * r + e[1].toFloat() * g + e[2].toFloat() * b
|
|
g_out = e[3].toFloat() * r + e[4].toFloat() * g + e[5].toFloat() * b
|
|
b_out = e[6].toFloat() * r + e[7].toFloat() * g + e[8].toFloat() * b
|
|
return (r_out, g_out, b_out)
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if isinstance(other, ColorSpaceTransform):
|
|
return all(abs(a.toFloat() - b.toFloat()) < 1e-6
|
|
for a, b in zip(self._elements, other._elements))
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(tuple(self._elements)) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return (f"ColorSpaceTransform([{self._elements[0]}, {self._elements[1]}, {self._elements[2]},"
|
|
f" {self._elements[3]}, {self._elements[4]}, {self._elements[5]},"
|
|
f" {self._elements[6]}, {self._elements[7]}, {self._elements[8]}])")
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Rational
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Rational(Stub):
|
|
"""Rational — 有理数"""
|
|
|
|
def __init__(self, numerator: int = 0, denominator: int = 1):
|
|
super().__init__()
|
|
if denominator < 0:
|
|
numerator = -numerator
|
|
denominator = -denominator
|
|
gcd = math.gcd(abs(numerator), abs(denominator)) if denominator != 0 else 1
|
|
self._numerator = numerator // gcd
|
|
self._denominator = denominator // gcd
|
|
|
|
def getNumerator(self) -> int:
|
|
return self._numerator
|
|
|
|
def getDenominator(self) -> int:
|
|
return self._denominator
|
|
|
|
def toFloat(self) -> float:
|
|
if self._denominator == 0:
|
|
return float('inf') if self._numerator >= 0 else float('-inf')
|
|
return self._numerator / self._denominator
|
|
|
|
def toDouble(self) -> float:
|
|
return self.toFloat()
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if isinstance(other, Rational):
|
|
return (self._numerator * other._denominator ==
|
|
other._numerator * self._denominator)
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._numerator, self._denominator)) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return f"{self._numerator}/{self._denominator}"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# StreamConfigurationMap
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class StreamConfigurationMap(Stub):
|
|
"""StreamConfigurationMap — 流配置映射"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._output_formats: Dict[int, List[Tuple[int, int]]] = {}
|
|
self._input_formats: Dict[int, List[Tuple[int, int]]] = {}
|
|
self._high_speed_video_sizes: Dict[int, List[Tuple[int, int]]] = {}
|
|
self._output_min_frame_durations: Dict[Tuple[int, int, int], int] = {}
|
|
self._output_stall_durations: Dict[Tuple[int, int, int], int] = {}
|
|
self._input_min_frame_durations: Dict[Tuple[int, int, int], int] = {}
|
|
self._input_stall_durations: Dict[Tuple[int, int, int], int] = {}
|
|
self._mutable: bool = True
|
|
|
|
def getOutputFormats(self) -> List[int]:
|
|
return list(self._output_formats.keys())
|
|
|
|
def getInputFormats(self) -> List[int]:
|
|
return list(self._input_formats.keys())
|
|
|
|
def getOutputSizes(self, format: int) -> List[Tuple[int, int]]:
|
|
return list(self._output_formats.get(format, []))
|
|
|
|
def getInputSizes(self, format: int) -> List[Tuple[int, int]]:
|
|
return list(self._input_formats.get(format, []))
|
|
|
|
def getHighSpeedVideoSizes(self) -> List[Tuple[int, int]]:
|
|
result = set()
|
|
for sizes in self._high_speed_video_sizes.values():
|
|
result.update(sizes)
|
|
return list(result)
|
|
|
|
def getHighSpeedVideoFpsRangeFor(self, size: Tuple[int, int]) -> List[Tuple[int, int]]:
|
|
return [(30, 30), (60, 60), (120, 120)]
|
|
|
|
def getHighSpeedVideoFpsRanges(self) -> List[Tuple[int, int]]:
|
|
return [(30, 30), (60, 60), (120, 120), (240, 240)]
|
|
|
|
def getOutputMinFrameDuration(self, format: int, size: Tuple[int, int]) -> int:
|
|
return self._output_min_frame_durations.get((format, size[0], size[1]), 33333333)
|
|
|
|
def getOutputStallDuration(self, format: int, size: Tuple[int, int]) -> int:
|
|
return self._output_stall_durations.get((format, size[0], size[1]), 0)
|
|
|
|
def getInputMinFrameDuration(self, format: int, size: Tuple[int, int]) -> int:
|
|
return self._input_min_frame_durations.get((format, size[0], size[1]), 33333333)
|
|
|
|
def getInputStallDuration(self, format: int, size: Tuple[int, int]) -> int:
|
|
return self._input_stall_durations.get((format, size[0], size[1]), 0)
|
|
|
|
def isOutputSupportedFor(self, size: Tuple[int, int]) -> bool:
|
|
for sizes in self._output_formats.values():
|
|
if size in sizes:
|
|
return True
|
|
return False
|
|
|
|
def isValid(self) -> bool:
|
|
return len(self._output_formats) > 0
|
|
|
|
def setMutable(self, mutable: bool):
|
|
self._mutable = mutable
|
|
|
|
def isMutable(self) -> bool:
|
|
return self._mutable
|
|
|
|
def addOutputFormat(self, format: int, sizes: List[Tuple[int, int]]):
|
|
self._output_formats[format] = list(sizes)
|
|
|
|
def addInputFormat(self, format: int, sizes: List[Tuple[int, int]]):
|
|
self._input_formats[format] = list(sizes)
|
|
|
|
def toString(self) -> str:
|
|
return f"StreamConfigurationMap(outputFormats={len(self._output_formats)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# TonemapCurve
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class TonemapCurve(Stub):
|
|
"""TonemapCurve — 色调映射曲线"""
|
|
|
|
LEVEL_BLACK = 0
|
|
LEVEL_WHITE = 1
|
|
|
|
CHANNEL_RED = 0
|
|
CHANNEL_GREEN = 1
|
|
CHANNEL_BLUE = 2
|
|
|
|
POINT_SIZE = 2
|
|
|
|
def __init__(self, red_curve: Optional[List[float]] = None,
|
|
green_curve: Optional[List[float]] = None,
|
|
blue_curve: Optional[List[float]] = None):
|
|
super().__init__()
|
|
self._curves = {
|
|
TonemapCurve.CHANNEL_RED: list(red_curve or [0.0, 0.0, 1.0, 1.0]),
|
|
TonemapCurve.CHANNEL_GREEN: list(green_curve or [0.0, 0.0, 1.0, 1.0]),
|
|
TonemapCurve.CHANNEL_BLUE: list(blue_curve or [0.0, 0.0, 1.0, 1.0]),
|
|
}
|
|
|
|
def getPointCount(self, channel: int) -> int:
|
|
return len(self._curves.get(channel, [])) // 2
|
|
|
|
def getPoint(self, channel: int, index: int, out_point: List[float] = None) -> Tuple[float, float]:
|
|
curve = self._curves.get(channel, [])
|
|
if out_point is None:
|
|
out_point = [0.0, 0.0]
|
|
out_point[0] = curve[index * 2]
|
|
out_point[1] = curve[index * 2 + 1]
|
|
return (out_point[0], out_point[1])
|
|
|
|
def setPoint(self, channel: int, index: int, x: float, y: float):
|
|
curve = self._curves[channel]
|
|
while len(curve) < (index * 2 + 2):
|
|
curve.extend([0.0, 0.0])
|
|
curve[index * 2] = x
|
|
curve[index * 2 + 1] = y
|
|
|
|
def getCurve(self, channel: int) -> Optional[List[float]]:
|
|
return list(self._curves.get(channel, []))
|
|
|
|
def estimateCurve(self, channel: int, x: float) -> float:
|
|
curve = self._curves.get(channel, [])
|
|
if not curve or len(curve) < 4:
|
|
return x
|
|
for i in range(0, len(curve) - 2, 2):
|
|
x0, y0 = curve[i], curve[i + 1]
|
|
x1, y1 = curve[i + 2], curve[i + 3]
|
|
if x0 <= x <= x1 or (x1 <= x <= x0):
|
|
if abs(x1 - x0) < 1e-10:
|
|
return y0
|
|
t = (x - x0) / (x1 - x0)
|
|
return y0 + t * (y1 - y0)
|
|
return curve[-1] if curve else x
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if isinstance(other, TonemapCurve):
|
|
return all(abs(self._curves[c][i] - other._curves[c][i]) < 1e-6
|
|
for c in range(3) for i in range(len(self._curves[c])))
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(tuple(tuple(v) for v in self._curves.values())) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return f"TonemapCurve(points={self.getPointCount(0)}, {self.getPointCount(1)}, {self.getPointCount(2)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# LensShadingMap
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class LensShadingMap(Stub):
|
|
"""LensShadingMap — 镜头阴影映射"""
|
|
|
|
def __init__(self, columns: int = 1, rows: int = 1):
|
|
super().__init__()
|
|
self._columns = columns
|
|
self._rows = rows
|
|
self._gain_map: Dict[Tuple[int, int, int], float] = {}
|
|
|
|
def getGainFactor(self, channel: int, column: int, row: int) -> float:
|
|
return self._gain_map.get((channel, column, row), 1.0)
|
|
|
|
def setGainFactor(self, channel: int, column: int, row: int, gain: float):
|
|
self._gain_map[(channel, column, row)] = gain
|
|
|
|
def getGainFactorVector(self, column: int, row: int) -> RggbChannelVector:
|
|
r = self.getGainFactor(0, column, row)
|
|
ge = self.getGainFactor(1, column, row)
|
|
go = self.getGainFactor(2, column, row)
|
|
b = self.getGainFactor(3, column, row)
|
|
return RggbChannelVector(r, ge, go, b)
|
|
|
|
def getColumnCount(self) -> int:
|
|
return self._columns
|
|
|
|
def getRowCount(self) -> int:
|
|
return self._rows
|
|
|
|
def copyGainFactors(self, destination: List[float], offset: int = 0):
|
|
for (ch, col, row), gain in self._gain_map.items():
|
|
idx = offset + (row * self._columns + col) * 4 + ch
|
|
if idx < len(destination):
|
|
destination[idx] = gain
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if isinstance(other, LensShadingMap):
|
|
return (self._columns == other._columns and self._rows == other._rows
|
|
and self._gain_map == other._gain_map)
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._columns, self._rows, tuple(sorted(self._gain_map.items())))) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return f"LensShadingMap({self._columns}x{self._rows})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MeteringRectangle
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MeteringRectangle(Stub):
|
|
"""MeteringRectangle — 测光矩形"""
|
|
|
|
def __init__(self, x: int = 0, y: int = 0, width: int = 0,
|
|
height: int = 0, metering_weight: int = 1):
|
|
super().__init__()
|
|
self._x = x
|
|
self._y = y
|
|
self._width = width
|
|
self._height = height
|
|
self._metering_weight = metering_weight
|
|
|
|
def getX(self) -> int:
|
|
return self._x
|
|
|
|
def getY(self) -> int:
|
|
return self._y
|
|
|
|
def getWidth(self) -> int:
|
|
return self._width
|
|
|
|
def getHeight(self) -> int:
|
|
return self._height
|
|
|
|
def getMeteringWeight(self) -> int:
|
|
return self._metering_weight
|
|
|
|
def getRect(self) -> Tuple[int, int, int, int]:
|
|
return (self._x, self._y, self._x + self._width, self._y + self._height)
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if isinstance(other, MeteringRectangle):
|
|
return (self._x == other._x and self._y == other._y
|
|
and self._width == other._width
|
|
and self._height == other._height
|
|
and self._metering_weight == other._metering_weight)
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._x, self._y, self._width, self._height, self._metering_weight)) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return f"MeteringRectangle({self._x},{self._y} {self._width}x{self._height} w={self._metering_weight})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# OutputConfiguration
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class OutputConfiguration(Stub):
|
|
"""OutputConfiguration — 输出配置"""
|
|
|
|
SURFACE_TYPE_UNKNOWN = -1
|
|
SURFACE_TYPE_SURFACE_VIEW = 0
|
|
SURFACE_TYPE_SURFACE_TEXTURE = 1
|
|
SURFACE_TYPE_IMAGE_READER = 2
|
|
SURFACE_TYPE_MEDIA_CODEC = 3
|
|
SURFACE_TYPE_MEDIA_RECORDER = 4
|
|
|
|
ROTATION_0 = 0
|
|
ROTATION_90 = 1
|
|
ROTATION_180 = 2
|
|
ROTATION_270 = 3
|
|
|
|
def __init__(self, surface: Any = None, surface_type: int = SURFACE_TYPE_UNKNOWN):
|
|
super().__init__()
|
|
self._surface = surface
|
|
self._surface_type = surface_type
|
|
self._rotation: int = OutputConfiguration.ROTATION_0
|
|
self._width: int = 0
|
|
self._height: int = 0
|
|
self._format: int = 0
|
|
self._is_deferred: bool = False
|
|
self._physical_camera_id: str = ""
|
|
self._surface_set: List[Any] = []
|
|
self._shared_surfaces: List[Any] = []
|
|
self._capture_stage: int = 0
|
|
|
|
def getSurface(self) -> Any:
|
|
return self._surface
|
|
|
|
def setSurface(self, surface: Any):
|
|
self._surface = surface
|
|
|
|
def getSurfaceType(self) -> int:
|
|
return self._surface_type
|
|
|
|
def setSurfaceType(self, type_: int):
|
|
self._surface_type = type_
|
|
|
|
def getRotation(self) -> int:
|
|
return self._rotation
|
|
|
|
def setRotation(self, rotation: int):
|
|
self._rotation = rotation
|
|
|
|
def getWidth(self) -> int:
|
|
return self._width
|
|
|
|
def getHeight(self) -> int:
|
|
return self._height
|
|
|
|
def getFormat(self) -> int:
|
|
return self._format
|
|
|
|
def setDimensions(self, width: int, height: int):
|
|
self._width = width
|
|
self._height = height
|
|
|
|
def setFormat(self, format: int):
|
|
self._format = format
|
|
|
|
def isDeferred(self) -> bool:
|
|
return self._is_deferred
|
|
|
|
def setDeferred(self, deferred: bool):
|
|
self._is_deferred = deferred
|
|
|
|
def getPhysicalCameraId(self) -> str:
|
|
return self._physical_camera_id
|
|
|
|
def setPhysicalCameraId(self, camera_id: str):
|
|
self._physical_camera_id = camera_id
|
|
|
|
def addSurface(self, surface: Any):
|
|
self._surface_set.append(surface)
|
|
|
|
def removeSurface(self, surface: Any):
|
|
self._surface_set = [s for s in self._surface_set if s is not surface]
|
|
|
|
def getSurfaces(self) -> List[Any]:
|
|
return list(self._surface_set)
|
|
|
|
def toString(self) -> str:
|
|
return f"OutputConfiguration({self._width}x{self._height}, rot={self._rotation})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# SessionConfiguration
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class SessionConfiguration(Stub):
|
|
"""SessionConfiguration — 会话配置"""
|
|
|
|
SESSION_TYPE_REGULAR = 0
|
|
SESSION_TYPE_VENDOR = 1
|
|
SESSION_TYPE_HIGH_SPEED = 2
|
|
SESSION_TYPE_LOW_LATENCY = 3
|
|
SESSION_TYPE_OFFLINE_PROCESSING = 4
|
|
|
|
def __init__(self, session_type: int = SESSION_TYPE_REGULAR,
|
|
outputs: Optional[List[OutputConfiguration]] = None,
|
|
callback: Any = None):
|
|
super().__init__()
|
|
self._session_type = session_type
|
|
self._outputs = list(outputs or [])
|
|
self._callback = callback
|
|
self._input_config: Optional[OutputConfiguration] = None
|
|
self._session_params: Any = None
|
|
self._implementation: Any = None
|
|
self._color_space: int = 0
|
|
self._is_global_session: bool = False
|
|
|
|
def getSessionType(self) -> int:
|
|
return self._session_type
|
|
|
|
def getOutputConfigurations(self) -> List[OutputConfiguration]:
|
|
return list(self._outputs)
|
|
|
|
def addOutputConfiguration(self, output: OutputConfiguration):
|
|
self._outputs.append(output)
|
|
|
|
def removeOutputConfiguration(self, output: OutputConfiguration):
|
|
self._outputs = [o for o in self._outputs if o is not output]
|
|
|
|
def setInputConfiguration(self, input_config: OutputConfiguration):
|
|
self._input_config = input_config
|
|
|
|
def getInputConfiguration(self) -> Optional[OutputConfiguration]:
|
|
return self._input_config
|
|
|
|
def setSessionParameters(self, params: Any):
|
|
self._session_params = params
|
|
|
|
def getSessionParameters(self) -> Any:
|
|
return self._session_params
|
|
|
|
def setColorSpace(self, color_space: int):
|
|
self._color_space = color_space
|
|
|
|
def getColorSpace(self) -> int:
|
|
return self._color_space
|
|
|
|
def getCallback(self) -> Any:
|
|
return self._callback
|
|
|
|
def toString(self) -> str:
|
|
return f"SessionConfiguration(type={self._session_type}, outputs={len(self._outputs)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MultiResolutionStreamConfigurationMap
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MultiResolutionStreamConfigurationMap(Stub):
|
|
"""MultiResolutionStreamConfigurationMap — 多分辨率流配置映射"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._configs: Dict[int, List[OutputConfiguration]] = {}
|
|
|
|
def getOutputConfigurations(self, format: int) -> List[OutputConfiguration]:
|
|
return list(self._configs.get(format, []))
|
|
|
|
def getFormats(self) -> List[int]:
|
|
return list(self._configs.keys())
|
|
|
|
def addConfiguration(self, format: int, config: OutputConfiguration):
|
|
if format not in self._configs:
|
|
self._configs[format] = []
|
|
self._configs[format].append(config)
|
|
|
|
def removeConfiguration(self, format: int, config: OutputConfiguration):
|
|
if format in self._configs:
|
|
self._configs[format] = [c for c in self._configs[format] if c is not config]
|
|
|
|
def getMaxResolution(self, format: int) -> Optional[OutputConfiguration]:
|
|
configs = self._configs.get(format, [])
|
|
if not configs:
|
|
return None
|
|
return max(configs, key=lambda c: c.getWidth() * c.getHeight())
|
|
|
|
def toString(self) -> str:
|
|
return f"MultiResolutionStreamConfigurationMap(formats={len(self._configs)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# OisSample
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class OisSample(Stub):
|
|
"""OisSample — OIS 样本数据"""
|
|
|
|
def __init__(self, timestamp_ns: int = 0, x_shift: float = 0.0, y_shift: float = 0.0):
|
|
super().__init__()
|
|
self._timestamp_ns = timestamp_ns
|
|
self._x_shift = x_shift
|
|
self._y_shift = y_shift
|
|
|
|
def getTimestampNanos(self) -> int:
|
|
return self._timestamp_ns
|
|
|
|
def getXshift(self) -> float:
|
|
return self._x_shift
|
|
|
|
def getYshift(self) -> float:
|
|
return self._y_shift
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if isinstance(other, OisSample):
|
|
return (self._timestamp_ns == other._timestamp_ns
|
|
and abs(self._x_shift - other._x_shift) < 1e-6
|
|
and abs(self._y_shift - other._y_shift) < 1e-6)
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._timestamp_ns, self._x_shift, self._y_shift)) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return f"OisSample(ts={self._timestamp_ns}, x={self._x_shift:.3f}, y={self._y_shift:.3f})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RecommendedStreamConfiguration
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RecommendedStreamConfiguration(Stub):
|
|
"""RecommendedStreamConfiguration — 推荐流配置"""
|
|
|
|
USECASE_PREVIEW = 0
|
|
USECASE_RECORD = 1
|
|
USECASE_STILL_CAPTURE = 2
|
|
USECASE_VIDEO_SNAPSHOT = 3
|
|
USECASE_ZSL = 4
|
|
USECASE_RAW = 5
|
|
USECASE_LOW_LATENCY_SNAPSHOT = 6
|
|
USECASE_10BIT_OUTPUT = 7
|
|
USECASE_ANALYSIS = 8
|
|
USECASE_CUSTOM = 9
|
|
USECASE_VENDOR_START = 0x1000
|
|
|
|
def __init__(self, width: int = 0, height: int = 0, format: int = 0,
|
|
input_output: bool = False, usecase: int = USECASE_PREVIEW):
|
|
super().__init__()
|
|
self._width = width
|
|
self._height = height
|
|
self._format = format
|
|
self._input_output = input_output
|
|
self._usecase = usecase
|
|
self._fps_range: Tuple[int, int] = (30, 30)
|
|
self._is_high_speed: bool = False
|
|
|
|
def getWidth(self) -> int:
|
|
return self._width
|
|
|
|
def getHeight(self) -> int:
|
|
return self._height
|
|
|
|
def getFormat(self) -> int:
|
|
return self._format
|
|
|
|
def isInput(self) -> bool:
|
|
return self._input_output
|
|
|
|
def isOutput(self) -> bool:
|
|
return not self._input_output
|
|
|
|
def getUsecase(self) -> int:
|
|
return self._usecase
|
|
|
|
def getFpsRange(self) -> Tuple[int, int]:
|
|
return self._fps_range
|
|
|
|
def setFpsRange(self, min_fps: int, max_fps: int):
|
|
self._fps_range = (min_fps, max_fps)
|
|
|
|
def isHighSpeed(self) -> bool:
|
|
return self._is_high_speed
|
|
|
|
def setHighSpeed(self, high_speed: bool):
|
|
self._is_high_speed = high_speed
|
|
|
|
def toString(self) -> str:
|
|
return f"RecommendedStreamConfiguration({self._width}x{self._height}, fmt={self._format})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RecommendedStreamConfigurationMap
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RecommendedStreamConfigurationMap(Stub):
|
|
"""RecommendedStreamConfigurationMap — 推荐流配置映射"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._recommendations: Dict[int, List[RecommendedStreamConfiguration]] = {}
|
|
self._max_resolutions: Dict[int, Tuple[int, int]] = {}
|
|
|
|
def getRecommendedStreamConfigurations(self, usecase: int) -> List[RecommendedStreamConfiguration]:
|
|
return list(self._recommendations.get(usecase, []))
|
|
|
|
def getAllUsecases(self) -> List[int]:
|
|
return list(self._recommendations.keys())
|
|
|
|
def addRecommendation(self, usecase: int, config: RecommendedStreamConfiguration):
|
|
if usecase not in self._recommendations:
|
|
self._recommendations[usecase] = []
|
|
self._recommendations[usecase].append(config)
|
|
|
|
def removeRecommendation(self, usecase: int, config: RecommendedStreamConfiguration):
|
|
if usecase in self._recommendations:
|
|
self._recommendations[usecase] = [
|
|
c for c in self._recommendations[usecase] if c is not config
|
|
]
|
|
|
|
def getMaxResolution(self, usecase: int) -> Optional[Tuple[int, int]]:
|
|
return self._max_resolutions.get(usecase)
|
|
|
|
def setMaxResolution(self, usecase: int, width: int, height: int):
|
|
self._max_resolutions[usecase] = (width, height)
|
|
|
|
def toString(self) -> str:
|
|
return f"RecommendedStreamConfigurationMap(usecases={len(self._recommendations)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# TotalCaptureResultExt — TotalCaptureResult 的扩展
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class TotalCaptureResultExt(Stub):
|
|
"""TotalCaptureResultExt — TotalCaptureResult 的 params 扩展"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._results: Dict[str, Any] = {}
|
|
self._partial_results: List[Any] = []
|
|
self._session_id: int = 0
|
|
self._frame_number: int = 0
|
|
self._sequence_id: int = 0
|
|
self._af_trigger_id: int = 0
|
|
self._ae_trigger_id: int = 0
|
|
self._sensor_timestamp: int = 0
|
|
self._physical_camera_results: Dict[str, Any] = {}
|
|
|
|
def getResult(self, key: str) -> Optional[Any]:
|
|
return self._results.get(key)
|
|
|
|
def setResult(self, key: str, value: Any):
|
|
self._results[key] = value
|
|
|
|
def getAllResults(self) -> Dict[str, Any]:
|
|
return dict(self._results)
|
|
|
|
def getPartialResults(self) -> List[Any]:
|
|
return list(self._partial_results)
|
|
|
|
def addPartialResult(self, result: Any):
|
|
self._partial_results.append(result)
|
|
|
|
def getSessionId(self) -> int:
|
|
return self._session_id
|
|
|
|
def setSessionId(self, session_id: int):
|
|
self._session_id = session_id
|
|
|
|
def getFrameNumber(self) -> int:
|
|
return self._frame_number
|
|
|
|
def setFrameNumber(self, frame: int):
|
|
self._frame_number = frame
|
|
|
|
def getSequenceId(self) -> int:
|
|
return self._sequence_id
|
|
|
|
def setSequenceId(self, seq: int):
|
|
self._sequence_id = seq
|
|
|
|
def getPhysicalCameraResult(self, camera_id: str) -> Optional[Any]:
|
|
return self._physical_camera_results.get(camera_id)
|
|
|
|
def setPhysicalCameraResult(self, camera_id: str, result: Any):
|
|
self._physical_camera_results[camera_id] = result
|
|
|
|
def toString(self) -> str:
|
|
return f"TotalCaptureResultExt(frame={self._frame_number}, seq={self._sequence_id})" |