mediagrains.grains.VideoGrain
1from collections.abc import MutableSequence, Mapping 2from fractions import Fraction 3 4from typing import ( 5 List, 6 Union, 7 Optional, 8 overload, 9 cast, 10 Iterator, 11 Iterable) 12from typing_extensions import Literal 13from uuid import UUID 14from deprecated import deprecated 15 16from mediatimestamp.immutable import Timestamp, SupportsMediaTimestamp, mediatimestamp 17from ..typing import ( 18 RationalTypes, 19 VideoGrainComponentDict, 20 FractionDict, 21 VideoGrainMetadataDict, 22 GrainDataParameterType) 23 24from ..cogenums import CogFrameFormat, CogFrameLayout 25from .Grain import Grain 26 27 28class VideoGrain(Grain): 29 """\ 30A class representing a raw video grain. 31 32Any grain can be freely cast to a tuple: 33 34 (meta, data) 35 36where meta is a dictionary containing the grain metadata, and data is the data element described below. 37 38The Grain class provides a number of properties which can be used to access 39parts of the standard grain metadata, and this class inherits these: 40 41meta 42 The meta dictionary object 43 44data 45 Either None or an object which can be cast to bytes by passing it to the bytes 46 constructor and will in of itself respond to the python-level portions of the bytes-like 47 object protocol. It is not guaranteed that this object will always respond correctly to the 48 C buffer-protocol, but it can always be converted into something that will by calling bytes on it. 49 50grain_type 51 A string containing the type of the grain, always "video" 52 53source_id 54 A uuid.UUID object representing the source_id in the grain 55 56flow_id 57 A uuid.UUID object representing the flow_id in the grain 58 59origin_timestamp 60 An mediatimestamp.Timestamp object representing the origin timestamp 61 of this grain. 62 63sync_timestamp 64 An mediatimestamp.Timestamp object representing the sync timestamp 65 of this grain. 66 67creation_timestamp 68 An mediatimestamp.Timestamp object representing the creation timestamp 69 of this grain. 70 71rate 72 A fractions.Fraction object representing the grain rate in grains per second. 73 74duration 75 A fractions.Fraction object representing the grain duration in seconds. 76 77timelabels 78 A list object containing time label data 79 80length 81 The length of the data element or 0 if that is None 82 83The VideoGrain class also provides additional properies 84 85format 86 An enumerated value of type CogFrameFormat 87 88width 89 The video width in pixels 90 91height 92 The video height in pixels 93 94layout 95 An enumerated value of type CogFrameLayout 96 97extension 98 A numeric value indicating the offset from the start of the data array to 99 the start of the actual data, usually 0. 100 101source_aspect_ratio 102 A fractions.Fraction object indicating the video source aspect ratio, or None 103 104pixel_aspect_ratio 105 A fractions.Fraction object indicating the video pixel aspect ratio, or None 106 107components 108 A list-like sequence of VideoGrain.COMPONENT objects 109 """ 110 111 class COMPONENT(Mapping): 112 """ 113A class representing a video component, it may be treated as a dictionary of the form: 114 115 {"stride": <an integer>, 116 "offset": <an integer>, 117 "width": <an integer>, 118 "height": <an integer>, 119 "length": <an integer>} 120 121with additional properties allowing access to the members: 122 123stride 124 The offset in bytes between the first data byte of each line in the data 125 array and the first byte of the next. 126 127offset 128 The offset in bytes from the start of the data array to the first byte of 129 the first line of the data in this component. 130 131width 132 The number of samples per line in this component 133 134height 135 The number of lines in this component 136 137length 138 The total length of the data for this component in bytes 139""" 140 def __init__(self, meta: VideoGrainComponentDict): 141 self.meta = meta 142 143 def __getitem__(self, key: Literal['stride', 'offset', 'width', 'height', 'length']) -> int: 144 return self.meta[key] 145 146 def __setitem__(self, key: Literal['stride', 'offset', 'width', 'height', 'length'], value: int) -> None: 147 self.meta[key] = value 148 149 def __iter__(self) -> Iterator[str]: 150 return self.meta.__iter__() 151 152 def __len__(self) -> int: 153 return self.meta.__len__() 154 155 def __eq__(self, other: object) -> bool: 156 return dict(self) == other 157 158 def __ne__(self, other: object) -> bool: 159 return not (self == other) 160 161 @property 162 def stride(self) -> int: 163 return self.meta['stride'] 164 165 @stride.setter 166 def stride(self, value: int) -> None: 167 self.meta['stride'] = value 168 169 @property 170 def offset(self) -> int: 171 return self.meta['offset'] 172 173 @offset.setter 174 def offset(self, value: int) -> None: 175 self.meta['offset'] = value 176 177 @property 178 def width(self) -> int: 179 return self.meta['width'] 180 181 @width.setter 182 def width(self, value: int) -> None: 183 self.meta['width'] = value 184 185 @property 186 def height(self) -> int: 187 return self.meta['height'] 188 189 @height.setter 190 def height(self, value: int) -> None: 191 self.meta['height'] = value 192 193 @property 194 def length(self) -> int: 195 return self.meta['length'] 196 197 @length.setter 198 def length(self, value: int) -> None: 199 self.meta['length'] = value 200 201 class COMPONENT_LIST(MutableSequence): 202 def __init__(self, parent: "VideoGrain"): 203 self.parent = parent 204 205 @overload 206 def __getitem__(self, key: int) -> "VideoGrain.COMPONENT": ... 207 208 @overload # noqa: F811 209 def __getitem__(self, key: slice) -> "List[VideoGrain.COMPONENT]": ... 210 211 def __getitem__(self, key): # noqa: F811 212 if isinstance(key, int): 213 return type(self.parent).COMPONENT(self.parent.meta['grain']['cog_frame']['components'][key]) 214 else: 215 return [type(self.parent).COMPONENT( 216 self.parent.meta['grain']['cog_frame']['components'][k]) for k in range(len(self))[key]] 217 218 @overload 219 def __setitem__(self, key: int, value: VideoGrainComponentDict) -> None: ... 220 221 @overload # noqa: F811 222 def __setitem__(self, key: slice, value: Iterable[VideoGrainComponentDict]) -> None: ... 223 224 def __setitem__(self, key, value): # noqa: F811 225 if isinstance(key, int): 226 self.parent.meta['grain']['cog_frame']['components'][key] = type(self.parent).COMPONENT(value) 227 else: 228 values = iter(value) 229 for n in range(len(self))[key]: 230 self.parent.meta['grain']['cog_frame']['components'][n] = type(self.parent).COMPONENT(next(values)) 231 232 def __delitem__(self, key: Union[int, slice]) -> None: 233 del self.parent.meta['grain']['cog_frame']['components'][key] 234 235 def insert(self, key: int, value: VideoGrainComponentDict) -> None: 236 self.parent.meta['grain']['cog_frame']['components'].insert( 237 key, type(self.parent).COMPONENT(value)) # type: ignore 238 239 def __len__(self) -> int: 240 return len(self.parent.meta['grain']['cog_frame']['components']) 241 242 def __eq__(self, other: object) -> bool: 243 return list(self) == other 244 245 def __ne__(self, other: object) -> bool: 246 return not (self == other) 247 248 def __init__(self, 249 meta: Optional[VideoGrainMetadataDict] = None, 250 data: Optional[GrainDataParameterType] = None, 251 src_id: Optional[UUID] = None, 252 flow_id: Optional[UUID] = None, 253 origin_timestamp: Optional[SupportsMediaTimestamp] = None, 254 creation_timestamp: Optional[SupportsMediaTimestamp] = None, 255 sync_timestamp: Optional[SupportsMediaTimestamp] = None, 256 rate: Fraction = Fraction(25, 1), 257 duration: Fraction = Fraction(1, 25), 258 cog_frame_format: CogFrameFormat = CogFrameFormat.UNKNOWN, 259 width: int = 1920, 260 height: int = 1080, 261 cog_frame_layout: CogFrameLayout = CogFrameLayout.UNKNOWN): 262 263 if meta is None: 264 if not isinstance(src_id, UUID) and src_id is not None: 265 raise AttributeError(f"src_id: Seen type {type(src_id)}, expected UUID.") 266 if not isinstance(flow_id, UUID) and flow_id is not None: 267 raise AttributeError(f"flow_id: Seen type {type(flow_id)}, expected UUID.") 268 269 if creation_timestamp is None: 270 creation_timestamp = Timestamp.get_time() 271 if origin_timestamp is None: 272 origin_timestamp = creation_timestamp 273 if sync_timestamp is None: 274 sync_timestamp = origin_timestamp 275 meta = { 276 "@_ns": "urn:x-ipstudio:ns:0.1", 277 "grain": { 278 'grain_type': "video", 279 'source_id': str(src_id), 280 'flow_id': str(flow_id), 281 'origin_timestamp': str(mediatimestamp(origin_timestamp)), 282 'sync_timestamp': str(mediatimestamp(sync_timestamp)), 283 'creation_timestamp': str(mediatimestamp(creation_timestamp)), 284 'rate': { 285 'numerator': Fraction(rate).numerator, 286 'denominator': Fraction(rate).denominator, 287 }, 288 'duration': { 289 'numerator': Fraction(duration).numerator, 290 'denominator': Fraction(duration).denominator, 291 }, 292 'cog_frame': { 293 "format": cog_frame_format, 294 "width": width, 295 "height": height, 296 "layout": cog_frame_layout, 297 "extension": 0, 298 "components": [] 299 } 300 }, 301 } 302 303 def size_for_format(fmt: CogFrameFormat, w: int, h: int) -> int: 304 if ((fmt >> 8) & 0x1) == 0x00: # Cog frame is not packed 305 h_shift = (fmt & 0x01) 306 v_shift = ((fmt >> 1) & 0x01) 307 depth = (fmt & 0xc) 308 if depth == 0: 309 bpv = 1 310 elif depth == 4: 311 bpv = 2 312 else: 313 bpv = 4 314 return (w*h + 2*((w*h) >> (h_shift + v_shift)))*bpv 315 else: 316 if fmt in (CogFrameFormat.YUYV, CogFrameFormat.UYVY, CogFrameFormat.AYUV): 317 return w*h*2 318 elif fmt in (CogFrameFormat.RGBx, 319 CogFrameFormat.RGBA, 320 CogFrameFormat.xRGB, 321 CogFrameFormat.ARGB, 322 CogFrameFormat.BGRx, 323 CogFrameFormat.BGRA, 324 CogFrameFormat.xBGR, 325 CogFrameFormat.ABGR): 326 return w*h*4 327 elif fmt == CogFrameFormat.RGB: 328 return w*h*3 329 elif fmt == CogFrameFormat.v210: 330 return h*(((w + 47) // 48) * 128) 331 elif fmt == CogFrameFormat.v216: 332 return w*h*4 333 else: 334 return 0 335 if data is None: 336 size = size_for_format(cog_frame_format, width, height) 337 data = bytearray(size) 338 339 def components_for_format(fmt: CogFrameFormat, w: int, h: int) -> List[VideoGrainComponentDict]: 340 components: List[VideoGrainComponentDict] = [] 341 if ((fmt >> 8) & 0x1) == 0x00: # Cog frame is not packed 342 h_shift = (fmt & 0x01) 343 v_shift = ((fmt >> 1) & 0x01) 344 depth = (fmt & 0xc) 345 if depth == 0: 346 bpv = 1 347 elif depth == 4: 348 bpv = 2 349 else: 350 bpv = 4 351 offset = 0 352 components.append({ 353 'stride': w*bpv, 354 'offset': offset, 355 'width': w, 356 'height': h, 357 'length': w*h*bpv 358 }) 359 offset += w*h*bpv 360 components.append({ 361 'stride': (w >> h_shift)*bpv, 362 'offset': offset, 363 'width': w >> h_shift, 364 'height': h >> v_shift, 365 'length': ((w*h) >> (h_shift + v_shift))*bpv 366 }) 367 offset += ((w*h) >> (h_shift + v_shift))*bpv 368 components.append({ 369 'stride': (w >> h_shift)*bpv, 370 'offset': offset, 371 'width': w >> h_shift, 372 'height': h >> v_shift, 373 'length': ((w*h) >> (h_shift + v_shift))*bpv 374 }) 375 offset += ((w*h) >> (h_shift + v_shift))*bpv 376 else: 377 if fmt in (CogFrameFormat.YUYV, CogFrameFormat.UYVY, CogFrameFormat.AYUV): 378 components.append({ 379 'stride': w*2, 380 'offset': 0, 381 'width': w, 382 'height': h, 383 'length': h*w*2 384 }) 385 elif fmt in (CogFrameFormat.RGBx, 386 CogFrameFormat.RGBA, 387 CogFrameFormat.xRGB, 388 CogFrameFormat.ARGB, 389 CogFrameFormat.BGRx, 390 CogFrameFormat.BGRA, 391 CogFrameFormat.xBGR, 392 CogFrameFormat.ABGR): 393 components.append({ 394 'stride': w*4, 395 'offset': 0, 396 'width': w, 397 'height': h, 398 'length': h*w*4 399 }) 400 elif fmt == CogFrameFormat.RGB: 401 components.append({ 402 'stride': w*3, 403 'offset': 0, 404 'width': w, 405 'height': h, 406 'length': h*w*3 407 }) 408 elif fmt == CogFrameFormat.v210: 409 components.append({ 410 'stride': (((w + 47) // 48) * 128), 411 'offset': 0, 412 'width': w, 413 'height': h, 414 'length': h*(((w + 47) // 48) * 128) 415 }) 416 elif fmt == CogFrameFormat.v216: 417 components.append({ 418 'stride': w*4, 419 'offset': 0, 420 'width': w, 421 'height': h, 422 'length': h*w*4 423 }) 424 return components 425 426 if ("cog_frame" in meta['grain'] and 427 ("components" not in meta['grain']['cog_frame'] or 428 len(meta['grain']['cog_frame']['components']) == 0)): 429 meta['grain']['cog_frame']['components'] = components_for_format(cog_frame_format, width, height) 430 431 super().__init__(meta=meta, data=data) 432 self.meta: VideoGrainMetadataDict 433 434 self._factory = "VideoGrain" 435 self.meta['grain']['grain_type'] = 'video' 436 if 'cog_frame' not in self.meta['grain']: 437 self.meta['grain']['cog_frame'] = { 438 'format': int(CogFrameFormat.UNKNOWN), 439 'width': 0, 440 'height': 0, 441 'layout': int(CogFrameLayout.UNKNOWN), 442 'extension': 0, 443 'components': [] 444 } 445 self.meta['grain']['cog_frame']['format'] = int(self.meta['grain']['cog_frame']['format']) 446 self.meta['grain']['cog_frame']['layout'] = int(self.meta['grain']['cog_frame']['layout']) 447 self.components = VideoGrain.COMPONENT_LIST(self) 448 449 @property 450 @deprecated(version="4.0.0", reason="Referencing `format` directly is deprecated in favour of `cog_frame_format`") 451 def format(self) -> CogFrameFormat: 452 return CogFrameFormat(self.meta['grain']['cog_frame']['format']) 453 454 @format.setter 455 @deprecated(version="4.0.0", reason="Referencing `format` directly is deprecated in favour of `cog_frame_format`") 456 def format(self, value: CogFrameFormat) -> None: 457 self.meta['grain']['cog_frame']['format'] = int(value) 458 459 @property 460 def cog_frame_format(self) -> CogFrameFormat: 461 return CogFrameFormat(self.meta['grain']['cog_frame']['format']) 462 463 @cog_frame_format.setter 464 def cog_frame_format(self, value: CogFrameFormat) -> None: 465 self.meta['grain']['cog_frame']['format'] = int(value) 466 467 @property 468 def width(self) -> int: 469 return self.meta['grain']['cog_frame']['width'] 470 471 @width.setter 472 def width(self, value: int) -> None: 473 self.meta['grain']['cog_frame']['width'] = value 474 475 @property 476 def height(self) -> int: 477 return self.meta['grain']['cog_frame']['height'] 478 479 @height.setter 480 def height(self, value: int) -> None: 481 self.meta['grain']['cog_frame']['height'] = value 482 483 @property 484 @deprecated(version="4.0.0", reason="Referencing `layout` directly is deprecated in favour of `cog_frame_layout`") 485 def layout(self) -> CogFrameLayout: 486 return CogFrameLayout(self.meta['grain']['cog_frame']['layout']) 487 488 @layout.setter 489 @deprecated(version="4.0.0", reason="Referencing `layout` directly is deprecated in favour of `cog_frame_layout`") 490 def layout(self, value: CogFrameLayout) -> None: 491 self.meta['grain']['cog_frame']['layout'] = int(value) 492 493 @property 494 def cog_frame_layout(self) -> CogFrameLayout: 495 return CogFrameLayout(self.meta['grain']['cog_frame']['layout']) 496 497 @cog_frame_layout.setter 498 def cog_frame_layout(self, value: CogFrameLayout) -> None: 499 self.meta['grain']['cog_frame']['layout'] = int(value) 500 501 @property 502 def extension(self) -> int: 503 return self.meta['grain']['cog_frame']['extension'] 504 505 @extension.setter 506 def extension(self, value: int) -> None: 507 self.meta['grain']['cog_frame']['extension'] = value 508 509 @property 510 def source_aspect_ratio(self) -> Optional[Fraction]: 511 if 'source_aspect_ratio' in self.meta['grain']['cog_frame']: 512 return Fraction(cast(FractionDict, self.meta['grain']['cog_frame']['source_aspect_ratio'])['numerator'], 513 cast(FractionDict, self.meta['grain']['cog_frame']['source_aspect_ratio'])['denominator']) 514 else: 515 return None 516 517 @source_aspect_ratio.setter 518 def source_aspect_ratio(self, value: RationalTypes) -> None: 519 value = Fraction(value) 520 self.meta['grain']['cog_frame']['source_aspect_ratio'] = {'numerator': value.numerator, 521 'denominator': value.denominator} 522 523 @property 524 def pixel_aspect_ratio(self) -> Optional[Fraction]: 525 if 'pixel_aspect_ratio' in self.meta['grain']['cog_frame']: 526 return Fraction(cast(FractionDict, self.meta['grain']['cog_frame']['pixel_aspect_ratio'])['numerator'], 527 cast(FractionDict, self.meta['grain']['cog_frame']['pixel_aspect_ratio'])['denominator']) 528 else: 529 return None 530 531 @pixel_aspect_ratio.setter 532 def pixel_aspect_ratio(self, value: RationalTypes) -> None: 533 value = Fraction(value) 534 self.meta['grain']['cog_frame']['pixel_aspect_ratio'] = {'numerator': value.numerator, 535 'denominator': value.denominator} 536 537 @property 538 def expected_length(self) -> int: 539 length = 0 540 for component in self.components: 541 if component.offset + component.length > length: 542 length = component.offset + component.length 543 return length 544 545 @property 546 def media_rate(self) -> Optional[Fraction]: 547 if self.rate: 548 return self.rate 549 else: 550 return None
29class VideoGrain(Grain): 30 """\ 31A class representing a raw video grain. 32 33Any grain can be freely cast to a tuple: 34 35 (meta, data) 36 37where meta is a dictionary containing the grain metadata, and data is the data element described below. 38 39The Grain class provides a number of properties which can be used to access 40parts of the standard grain metadata, and this class inherits these: 41 42meta 43 The meta dictionary object 44 45data 46 Either None or an object which can be cast to bytes by passing it to the bytes 47 constructor and will in of itself respond to the python-level portions of the bytes-like 48 object protocol. It is not guaranteed that this object will always respond correctly to the 49 C buffer-protocol, but it can always be converted into something that will by calling bytes on it. 50 51grain_type 52 A string containing the type of the grain, always "video" 53 54source_id 55 A uuid.UUID object representing the source_id in the grain 56 57flow_id 58 A uuid.UUID object representing the flow_id in the grain 59 60origin_timestamp 61 An mediatimestamp.Timestamp object representing the origin timestamp 62 of this grain. 63 64sync_timestamp 65 An mediatimestamp.Timestamp object representing the sync timestamp 66 of this grain. 67 68creation_timestamp 69 An mediatimestamp.Timestamp object representing the creation timestamp 70 of this grain. 71 72rate 73 A fractions.Fraction object representing the grain rate in grains per second. 74 75duration 76 A fractions.Fraction object representing the grain duration in seconds. 77 78timelabels 79 A list object containing time label data 80 81length 82 The length of the data element or 0 if that is None 83 84The VideoGrain class also provides additional properies 85 86format 87 An enumerated value of type CogFrameFormat 88 89width 90 The video width in pixels 91 92height 93 The video height in pixels 94 95layout 96 An enumerated value of type CogFrameLayout 97 98extension 99 A numeric value indicating the offset from the start of the data array to 100 the start of the actual data, usually 0. 101 102source_aspect_ratio 103 A fractions.Fraction object indicating the video source aspect ratio, or None 104 105pixel_aspect_ratio 106 A fractions.Fraction object indicating the video pixel aspect ratio, or None 107 108components 109 A list-like sequence of VideoGrain.COMPONENT objects 110 """ 111 112 class COMPONENT(Mapping): 113 """ 114A class representing a video component, it may be treated as a dictionary of the form: 115 116 {"stride": <an integer>, 117 "offset": <an integer>, 118 "width": <an integer>, 119 "height": <an integer>, 120 "length": <an integer>} 121 122with additional properties allowing access to the members: 123 124stride 125 The offset in bytes between the first data byte of each line in the data 126 array and the first byte of the next. 127 128offset 129 The offset in bytes from the start of the data array to the first byte of 130 the first line of the data in this component. 131 132width 133 The number of samples per line in this component 134 135height 136 The number of lines in this component 137 138length 139 The total length of the data for this component in bytes 140""" 141 def __init__(self, meta: VideoGrainComponentDict): 142 self.meta = meta 143 144 def __getitem__(self, key: Literal['stride', 'offset', 'width', 'height', 'length']) -> int: 145 return self.meta[key] 146 147 def __setitem__(self, key: Literal['stride', 'offset', 'width', 'height', 'length'], value: int) -> None: 148 self.meta[key] = value 149 150 def __iter__(self) -> Iterator[str]: 151 return self.meta.__iter__() 152 153 def __len__(self) -> int: 154 return self.meta.__len__() 155 156 def __eq__(self, other: object) -> bool: 157 return dict(self) == other 158 159 def __ne__(self, other: object) -> bool: 160 return not (self == other) 161 162 @property 163 def stride(self) -> int: 164 return self.meta['stride'] 165 166 @stride.setter 167 def stride(self, value: int) -> None: 168 self.meta['stride'] = value 169 170 @property 171 def offset(self) -> int: 172 return self.meta['offset'] 173 174 @offset.setter 175 def offset(self, value: int) -> None: 176 self.meta['offset'] = value 177 178 @property 179 def width(self) -> int: 180 return self.meta['width'] 181 182 @width.setter 183 def width(self, value: int) -> None: 184 self.meta['width'] = value 185 186 @property 187 def height(self) -> int: 188 return self.meta['height'] 189 190 @height.setter 191 def height(self, value: int) -> None: 192 self.meta['height'] = value 193 194 @property 195 def length(self) -> int: 196 return self.meta['length'] 197 198 @length.setter 199 def length(self, value: int) -> None: 200 self.meta['length'] = value 201 202 class COMPONENT_LIST(MutableSequence): 203 def __init__(self, parent: "VideoGrain"): 204 self.parent = parent 205 206 @overload 207 def __getitem__(self, key: int) -> "VideoGrain.COMPONENT": ... 208 209 @overload # noqa: F811 210 def __getitem__(self, key: slice) -> "List[VideoGrain.COMPONENT]": ... 211 212 def __getitem__(self, key): # noqa: F811 213 if isinstance(key, int): 214 return type(self.parent).COMPONENT(self.parent.meta['grain']['cog_frame']['components'][key]) 215 else: 216 return [type(self.parent).COMPONENT( 217 self.parent.meta['grain']['cog_frame']['components'][k]) for k in range(len(self))[key]] 218 219 @overload 220 def __setitem__(self, key: int, value: VideoGrainComponentDict) -> None: ... 221 222 @overload # noqa: F811 223 def __setitem__(self, key: slice, value: Iterable[VideoGrainComponentDict]) -> None: ... 224 225 def __setitem__(self, key, value): # noqa: F811 226 if isinstance(key, int): 227 self.parent.meta['grain']['cog_frame']['components'][key] = type(self.parent).COMPONENT(value) 228 else: 229 values = iter(value) 230 for n in range(len(self))[key]: 231 self.parent.meta['grain']['cog_frame']['components'][n] = type(self.parent).COMPONENT(next(values)) 232 233 def __delitem__(self, key: Union[int, slice]) -> None: 234 del self.parent.meta['grain']['cog_frame']['components'][key] 235 236 def insert(self, key: int, value: VideoGrainComponentDict) -> None: 237 self.parent.meta['grain']['cog_frame']['components'].insert( 238 key, type(self.parent).COMPONENT(value)) # type: ignore 239 240 def __len__(self) -> int: 241 return len(self.parent.meta['grain']['cog_frame']['components']) 242 243 def __eq__(self, other: object) -> bool: 244 return list(self) == other 245 246 def __ne__(self, other: object) -> bool: 247 return not (self == other) 248 249 def __init__(self, 250 meta: Optional[VideoGrainMetadataDict] = None, 251 data: Optional[GrainDataParameterType] = None, 252 src_id: Optional[UUID] = None, 253 flow_id: Optional[UUID] = None, 254 origin_timestamp: Optional[SupportsMediaTimestamp] = None, 255 creation_timestamp: Optional[SupportsMediaTimestamp] = None, 256 sync_timestamp: Optional[SupportsMediaTimestamp] = None, 257 rate: Fraction = Fraction(25, 1), 258 duration: Fraction = Fraction(1, 25), 259 cog_frame_format: CogFrameFormat = CogFrameFormat.UNKNOWN, 260 width: int = 1920, 261 height: int = 1080, 262 cog_frame_layout: CogFrameLayout = CogFrameLayout.UNKNOWN): 263 264 if meta is None: 265 if not isinstance(src_id, UUID) and src_id is not None: 266 raise AttributeError(f"src_id: Seen type {type(src_id)}, expected UUID.") 267 if not isinstance(flow_id, UUID) and flow_id is not None: 268 raise AttributeError(f"flow_id: Seen type {type(flow_id)}, expected UUID.") 269 270 if creation_timestamp is None: 271 creation_timestamp = Timestamp.get_time() 272 if origin_timestamp is None: 273 origin_timestamp = creation_timestamp 274 if sync_timestamp is None: 275 sync_timestamp = origin_timestamp 276 meta = { 277 "@_ns": "urn:x-ipstudio:ns:0.1", 278 "grain": { 279 'grain_type': "video", 280 'source_id': str(src_id), 281 'flow_id': str(flow_id), 282 'origin_timestamp': str(mediatimestamp(origin_timestamp)), 283 'sync_timestamp': str(mediatimestamp(sync_timestamp)), 284 'creation_timestamp': str(mediatimestamp(creation_timestamp)), 285 'rate': { 286 'numerator': Fraction(rate).numerator, 287 'denominator': Fraction(rate).denominator, 288 }, 289 'duration': { 290 'numerator': Fraction(duration).numerator, 291 'denominator': Fraction(duration).denominator, 292 }, 293 'cog_frame': { 294 "format": cog_frame_format, 295 "width": width, 296 "height": height, 297 "layout": cog_frame_layout, 298 "extension": 0, 299 "components": [] 300 } 301 }, 302 } 303 304 def size_for_format(fmt: CogFrameFormat, w: int, h: int) -> int: 305 if ((fmt >> 8) & 0x1) == 0x00: # Cog frame is not packed 306 h_shift = (fmt & 0x01) 307 v_shift = ((fmt >> 1) & 0x01) 308 depth = (fmt & 0xc) 309 if depth == 0: 310 bpv = 1 311 elif depth == 4: 312 bpv = 2 313 else: 314 bpv = 4 315 return (w*h + 2*((w*h) >> (h_shift + v_shift)))*bpv 316 else: 317 if fmt in (CogFrameFormat.YUYV, CogFrameFormat.UYVY, CogFrameFormat.AYUV): 318 return w*h*2 319 elif fmt in (CogFrameFormat.RGBx, 320 CogFrameFormat.RGBA, 321 CogFrameFormat.xRGB, 322 CogFrameFormat.ARGB, 323 CogFrameFormat.BGRx, 324 CogFrameFormat.BGRA, 325 CogFrameFormat.xBGR, 326 CogFrameFormat.ABGR): 327 return w*h*4 328 elif fmt == CogFrameFormat.RGB: 329 return w*h*3 330 elif fmt == CogFrameFormat.v210: 331 return h*(((w + 47) // 48) * 128) 332 elif fmt == CogFrameFormat.v216: 333 return w*h*4 334 else: 335 return 0 336 if data is None: 337 size = size_for_format(cog_frame_format, width, height) 338 data = bytearray(size) 339 340 def components_for_format(fmt: CogFrameFormat, w: int, h: int) -> List[VideoGrainComponentDict]: 341 components: List[VideoGrainComponentDict] = [] 342 if ((fmt >> 8) & 0x1) == 0x00: # Cog frame is not packed 343 h_shift = (fmt & 0x01) 344 v_shift = ((fmt >> 1) & 0x01) 345 depth = (fmt & 0xc) 346 if depth == 0: 347 bpv = 1 348 elif depth == 4: 349 bpv = 2 350 else: 351 bpv = 4 352 offset = 0 353 components.append({ 354 'stride': w*bpv, 355 'offset': offset, 356 'width': w, 357 'height': h, 358 'length': w*h*bpv 359 }) 360 offset += w*h*bpv 361 components.append({ 362 'stride': (w >> h_shift)*bpv, 363 'offset': offset, 364 'width': w >> h_shift, 365 'height': h >> v_shift, 366 'length': ((w*h) >> (h_shift + v_shift))*bpv 367 }) 368 offset += ((w*h) >> (h_shift + v_shift))*bpv 369 components.append({ 370 'stride': (w >> h_shift)*bpv, 371 'offset': offset, 372 'width': w >> h_shift, 373 'height': h >> v_shift, 374 'length': ((w*h) >> (h_shift + v_shift))*bpv 375 }) 376 offset += ((w*h) >> (h_shift + v_shift))*bpv 377 else: 378 if fmt in (CogFrameFormat.YUYV, CogFrameFormat.UYVY, CogFrameFormat.AYUV): 379 components.append({ 380 'stride': w*2, 381 'offset': 0, 382 'width': w, 383 'height': h, 384 'length': h*w*2 385 }) 386 elif fmt in (CogFrameFormat.RGBx, 387 CogFrameFormat.RGBA, 388 CogFrameFormat.xRGB, 389 CogFrameFormat.ARGB, 390 CogFrameFormat.BGRx, 391 CogFrameFormat.BGRA, 392 CogFrameFormat.xBGR, 393 CogFrameFormat.ABGR): 394 components.append({ 395 'stride': w*4, 396 'offset': 0, 397 'width': w, 398 'height': h, 399 'length': h*w*4 400 }) 401 elif fmt == CogFrameFormat.RGB: 402 components.append({ 403 'stride': w*3, 404 'offset': 0, 405 'width': w, 406 'height': h, 407 'length': h*w*3 408 }) 409 elif fmt == CogFrameFormat.v210: 410 components.append({ 411 'stride': (((w + 47) // 48) * 128), 412 'offset': 0, 413 'width': w, 414 'height': h, 415 'length': h*(((w + 47) // 48) * 128) 416 }) 417 elif fmt == CogFrameFormat.v216: 418 components.append({ 419 'stride': w*4, 420 'offset': 0, 421 'width': w, 422 'height': h, 423 'length': h*w*4 424 }) 425 return components 426 427 if ("cog_frame" in meta['grain'] and 428 ("components" not in meta['grain']['cog_frame'] or 429 len(meta['grain']['cog_frame']['components']) == 0)): 430 meta['grain']['cog_frame']['components'] = components_for_format(cog_frame_format, width, height) 431 432 super().__init__(meta=meta, data=data) 433 self.meta: VideoGrainMetadataDict 434 435 self._factory = "VideoGrain" 436 self.meta['grain']['grain_type'] = 'video' 437 if 'cog_frame' not in self.meta['grain']: 438 self.meta['grain']['cog_frame'] = { 439 'format': int(CogFrameFormat.UNKNOWN), 440 'width': 0, 441 'height': 0, 442 'layout': int(CogFrameLayout.UNKNOWN), 443 'extension': 0, 444 'components': [] 445 } 446 self.meta['grain']['cog_frame']['format'] = int(self.meta['grain']['cog_frame']['format']) 447 self.meta['grain']['cog_frame']['layout'] = int(self.meta['grain']['cog_frame']['layout']) 448 self.components = VideoGrain.COMPONENT_LIST(self) 449 450 @property 451 @deprecated(version="4.0.0", reason="Referencing `format` directly is deprecated in favour of `cog_frame_format`") 452 def format(self) -> CogFrameFormat: 453 return CogFrameFormat(self.meta['grain']['cog_frame']['format']) 454 455 @format.setter 456 @deprecated(version="4.0.0", reason="Referencing `format` directly is deprecated in favour of `cog_frame_format`") 457 def format(self, value: CogFrameFormat) -> None: 458 self.meta['grain']['cog_frame']['format'] = int(value) 459 460 @property 461 def cog_frame_format(self) -> CogFrameFormat: 462 return CogFrameFormat(self.meta['grain']['cog_frame']['format']) 463 464 @cog_frame_format.setter 465 def cog_frame_format(self, value: CogFrameFormat) -> None: 466 self.meta['grain']['cog_frame']['format'] = int(value) 467 468 @property 469 def width(self) -> int: 470 return self.meta['grain']['cog_frame']['width'] 471 472 @width.setter 473 def width(self, value: int) -> None: 474 self.meta['grain']['cog_frame']['width'] = value 475 476 @property 477 def height(self) -> int: 478 return self.meta['grain']['cog_frame']['height'] 479 480 @height.setter 481 def height(self, value: int) -> None: 482 self.meta['grain']['cog_frame']['height'] = value 483 484 @property 485 @deprecated(version="4.0.0", reason="Referencing `layout` directly is deprecated in favour of `cog_frame_layout`") 486 def layout(self) -> CogFrameLayout: 487 return CogFrameLayout(self.meta['grain']['cog_frame']['layout']) 488 489 @layout.setter 490 @deprecated(version="4.0.0", reason="Referencing `layout` directly is deprecated in favour of `cog_frame_layout`") 491 def layout(self, value: CogFrameLayout) -> None: 492 self.meta['grain']['cog_frame']['layout'] = int(value) 493 494 @property 495 def cog_frame_layout(self) -> CogFrameLayout: 496 return CogFrameLayout(self.meta['grain']['cog_frame']['layout']) 497 498 @cog_frame_layout.setter 499 def cog_frame_layout(self, value: CogFrameLayout) -> None: 500 self.meta['grain']['cog_frame']['layout'] = int(value) 501 502 @property 503 def extension(self) -> int: 504 return self.meta['grain']['cog_frame']['extension'] 505 506 @extension.setter 507 def extension(self, value: int) -> None: 508 self.meta['grain']['cog_frame']['extension'] = value 509 510 @property 511 def source_aspect_ratio(self) -> Optional[Fraction]: 512 if 'source_aspect_ratio' in self.meta['grain']['cog_frame']: 513 return Fraction(cast(FractionDict, self.meta['grain']['cog_frame']['source_aspect_ratio'])['numerator'], 514 cast(FractionDict, self.meta['grain']['cog_frame']['source_aspect_ratio'])['denominator']) 515 else: 516 return None 517 518 @source_aspect_ratio.setter 519 def source_aspect_ratio(self, value: RationalTypes) -> None: 520 value = Fraction(value) 521 self.meta['grain']['cog_frame']['source_aspect_ratio'] = {'numerator': value.numerator, 522 'denominator': value.denominator} 523 524 @property 525 def pixel_aspect_ratio(self) -> Optional[Fraction]: 526 if 'pixel_aspect_ratio' in self.meta['grain']['cog_frame']: 527 return Fraction(cast(FractionDict, self.meta['grain']['cog_frame']['pixel_aspect_ratio'])['numerator'], 528 cast(FractionDict, self.meta['grain']['cog_frame']['pixel_aspect_ratio'])['denominator']) 529 else: 530 return None 531 532 @pixel_aspect_ratio.setter 533 def pixel_aspect_ratio(self, value: RationalTypes) -> None: 534 value = Fraction(value) 535 self.meta['grain']['cog_frame']['pixel_aspect_ratio'] = {'numerator': value.numerator, 536 'denominator': value.denominator} 537 538 @property 539 def expected_length(self) -> int: 540 length = 0 541 for component in self.components: 542 if component.offset + component.length > length: 543 length = component.offset + component.length 544 return length 545 546 @property 547 def media_rate(self) -> Optional[Fraction]: 548 if self.rate: 549 return self.rate 550 else: 551 return None
A class representing a raw video grain.
Any grain can be freely cast to a tuple:
(meta, data)
where meta is a dictionary containing the grain metadata, and data is the data element described below.
The Grain class provides a number of properties which can be used to access parts of the standard grain metadata, and this class inherits these:
meta The meta dictionary object
data Either None or an object which can be cast to bytes by passing it to the bytes constructor and will in of itself respond to the python-level portions of the bytes-like object protocol. It is not guaranteed that this object will always respond correctly to the C buffer-protocol, but it can always be converted into something that will by calling bytes on it.
grain_type A string containing the type of the grain, always "video"
source_id A uuid.UUID object representing the source_id in the grain
flow_id A uuid.UUID object representing the flow_id in the grain
origin_timestamp An mediatimestamp.Timestamp object representing the origin timestamp of this grain.
sync_timestamp An mediatimestamp.Timestamp object representing the sync timestamp of this grain.
creation_timestamp An mediatimestamp.Timestamp object representing the creation timestamp of this grain.
rate A fractions.Fraction object representing the grain rate in grains per second.
duration A fractions.Fraction object representing the grain duration in seconds.
timelabels A list object containing time label data
length The length of the data element or 0 if that is None
The VideoGrain class also provides additional properies
format An enumerated value of type CogFrameFormat
width The video width in pixels
height The video height in pixels
layout An enumerated value of type CogFrameLayout
extension A numeric value indicating the offset from the start of the data array to the start of the actual data, usually 0.
source_aspect_ratio A fractions.Fraction object indicating the video source aspect ratio, or None
pixel_aspect_ratio A fractions.Fraction object indicating the video pixel aspect ratio, or None
components A list-like sequence of VideoGrain.COMPONENT objects
249 def __init__(self, 250 meta: Optional[VideoGrainMetadataDict] = None, 251 data: Optional[GrainDataParameterType] = None, 252 src_id: Optional[UUID] = None, 253 flow_id: Optional[UUID] = None, 254 origin_timestamp: Optional[SupportsMediaTimestamp] = None, 255 creation_timestamp: Optional[SupportsMediaTimestamp] = None, 256 sync_timestamp: Optional[SupportsMediaTimestamp] = None, 257 rate: Fraction = Fraction(25, 1), 258 duration: Fraction = Fraction(1, 25), 259 cog_frame_format: CogFrameFormat = CogFrameFormat.UNKNOWN, 260 width: int = 1920, 261 height: int = 1080, 262 cog_frame_layout: CogFrameLayout = CogFrameLayout.UNKNOWN): 263 264 if meta is None: 265 if not isinstance(src_id, UUID) and src_id is not None: 266 raise AttributeError(f"src_id: Seen type {type(src_id)}, expected UUID.") 267 if not isinstance(flow_id, UUID) and flow_id is not None: 268 raise AttributeError(f"flow_id: Seen type {type(flow_id)}, expected UUID.") 269 270 if creation_timestamp is None: 271 creation_timestamp = Timestamp.get_time() 272 if origin_timestamp is None: 273 origin_timestamp = creation_timestamp 274 if sync_timestamp is None: 275 sync_timestamp = origin_timestamp 276 meta = { 277 "@_ns": "urn:x-ipstudio:ns:0.1", 278 "grain": { 279 'grain_type': "video", 280 'source_id': str(src_id), 281 'flow_id': str(flow_id), 282 'origin_timestamp': str(mediatimestamp(origin_timestamp)), 283 'sync_timestamp': str(mediatimestamp(sync_timestamp)), 284 'creation_timestamp': str(mediatimestamp(creation_timestamp)), 285 'rate': { 286 'numerator': Fraction(rate).numerator, 287 'denominator': Fraction(rate).denominator, 288 }, 289 'duration': { 290 'numerator': Fraction(duration).numerator, 291 'denominator': Fraction(duration).denominator, 292 }, 293 'cog_frame': { 294 "format": cog_frame_format, 295 "width": width, 296 "height": height, 297 "layout": cog_frame_layout, 298 "extension": 0, 299 "components": [] 300 } 301 }, 302 } 303 304 def size_for_format(fmt: CogFrameFormat, w: int, h: int) -> int: 305 if ((fmt >> 8) & 0x1) == 0x00: # Cog frame is not packed 306 h_shift = (fmt & 0x01) 307 v_shift = ((fmt >> 1) & 0x01) 308 depth = (fmt & 0xc) 309 if depth == 0: 310 bpv = 1 311 elif depth == 4: 312 bpv = 2 313 else: 314 bpv = 4 315 return (w*h + 2*((w*h) >> (h_shift + v_shift)))*bpv 316 else: 317 if fmt in (CogFrameFormat.YUYV, CogFrameFormat.UYVY, CogFrameFormat.AYUV): 318 return w*h*2 319 elif fmt in (CogFrameFormat.RGBx, 320 CogFrameFormat.RGBA, 321 CogFrameFormat.xRGB, 322 CogFrameFormat.ARGB, 323 CogFrameFormat.BGRx, 324 CogFrameFormat.BGRA, 325 CogFrameFormat.xBGR, 326 CogFrameFormat.ABGR): 327 return w*h*4 328 elif fmt == CogFrameFormat.RGB: 329 return w*h*3 330 elif fmt == CogFrameFormat.v210: 331 return h*(((w + 47) // 48) * 128) 332 elif fmt == CogFrameFormat.v216: 333 return w*h*4 334 else: 335 return 0 336 if data is None: 337 size = size_for_format(cog_frame_format, width, height) 338 data = bytearray(size) 339 340 def components_for_format(fmt: CogFrameFormat, w: int, h: int) -> List[VideoGrainComponentDict]: 341 components: List[VideoGrainComponentDict] = [] 342 if ((fmt >> 8) & 0x1) == 0x00: # Cog frame is not packed 343 h_shift = (fmt & 0x01) 344 v_shift = ((fmt >> 1) & 0x01) 345 depth = (fmt & 0xc) 346 if depth == 0: 347 bpv = 1 348 elif depth == 4: 349 bpv = 2 350 else: 351 bpv = 4 352 offset = 0 353 components.append({ 354 'stride': w*bpv, 355 'offset': offset, 356 'width': w, 357 'height': h, 358 'length': w*h*bpv 359 }) 360 offset += w*h*bpv 361 components.append({ 362 'stride': (w >> h_shift)*bpv, 363 'offset': offset, 364 'width': w >> h_shift, 365 'height': h >> v_shift, 366 'length': ((w*h) >> (h_shift + v_shift))*bpv 367 }) 368 offset += ((w*h) >> (h_shift + v_shift))*bpv 369 components.append({ 370 'stride': (w >> h_shift)*bpv, 371 'offset': offset, 372 'width': w >> h_shift, 373 'height': h >> v_shift, 374 'length': ((w*h) >> (h_shift + v_shift))*bpv 375 }) 376 offset += ((w*h) >> (h_shift + v_shift))*bpv 377 else: 378 if fmt in (CogFrameFormat.YUYV, CogFrameFormat.UYVY, CogFrameFormat.AYUV): 379 components.append({ 380 'stride': w*2, 381 'offset': 0, 382 'width': w, 383 'height': h, 384 'length': h*w*2 385 }) 386 elif fmt in (CogFrameFormat.RGBx, 387 CogFrameFormat.RGBA, 388 CogFrameFormat.xRGB, 389 CogFrameFormat.ARGB, 390 CogFrameFormat.BGRx, 391 CogFrameFormat.BGRA, 392 CogFrameFormat.xBGR, 393 CogFrameFormat.ABGR): 394 components.append({ 395 'stride': w*4, 396 'offset': 0, 397 'width': w, 398 'height': h, 399 'length': h*w*4 400 }) 401 elif fmt == CogFrameFormat.RGB: 402 components.append({ 403 'stride': w*3, 404 'offset': 0, 405 'width': w, 406 'height': h, 407 'length': h*w*3 408 }) 409 elif fmt == CogFrameFormat.v210: 410 components.append({ 411 'stride': (((w + 47) // 48) * 128), 412 'offset': 0, 413 'width': w, 414 'height': h, 415 'length': h*(((w + 47) // 48) * 128) 416 }) 417 elif fmt == CogFrameFormat.v216: 418 components.append({ 419 'stride': w*4, 420 'offset': 0, 421 'width': w, 422 'height': h, 423 'length': h*w*4 424 }) 425 return components 426 427 if ("cog_frame" in meta['grain'] and 428 ("components" not in meta['grain']['cog_frame'] or 429 len(meta['grain']['cog_frame']['components']) == 0)): 430 meta['grain']['cog_frame']['components'] = components_for_format(cog_frame_format, width, height) 431 432 super().__init__(meta=meta, data=data) 433 self.meta: VideoGrainMetadataDict 434 435 self._factory = "VideoGrain" 436 self.meta['grain']['grain_type'] = 'video' 437 if 'cog_frame' not in self.meta['grain']: 438 self.meta['grain']['cog_frame'] = { 439 'format': int(CogFrameFormat.UNKNOWN), 440 'width': 0, 441 'height': 0, 442 'layout': int(CogFrameLayout.UNKNOWN), 443 'extension': 0, 444 'components': [] 445 } 446 self.meta['grain']['cog_frame']['format'] = int(self.meta['grain']['cog_frame']['format']) 447 self.meta['grain']['cog_frame']['layout'] = int(self.meta['grain']['cog_frame']['layout']) 448 self.components = VideoGrain.COMPONENT_LIST(self)
510 @property 511 def source_aspect_ratio(self) -> Optional[Fraction]: 512 if 'source_aspect_ratio' in self.meta['grain']['cog_frame']: 513 return Fraction(cast(FractionDict, self.meta['grain']['cog_frame']['source_aspect_ratio'])['numerator'], 514 cast(FractionDict, self.meta['grain']['cog_frame']['source_aspect_ratio'])['denominator']) 515 else: 516 return None
524 @property 525 def pixel_aspect_ratio(self) -> Optional[Fraction]: 526 if 'pixel_aspect_ratio' in self.meta['grain']['cog_frame']: 527 return Fraction(cast(FractionDict, self.meta['grain']['cog_frame']['pixel_aspect_ratio'])['numerator'], 528 cast(FractionDict, self.meta['grain']['cog_frame']['pixel_aspect_ratio'])['denominator']) 529 else: 530 return None
Inherited Members
- mediagrains.grains.Grain.Grain
- has_data
- data
- grain_type
- source_id
- src_id
- flow_id
- origin_timestamp
- final_origin_timestamp
- origin_timerange
- presentation_origin_timestamp
- final_presentation_origin_timestamp
- presentation_origin_timerange
- normalise_time
- sync_timestamp
- creation_timestamp
- rate
- duration
- timelabels
- add_timelabel
- TIMELABEL
- TIMELABELS
- length
- collections.abc.Sequence
- index
- count
112 class COMPONENT(Mapping): 113 """ 114A class representing a video component, it may be treated as a dictionary of the form: 115 116 {"stride": <an integer>, 117 "offset": <an integer>, 118 "width": <an integer>, 119 "height": <an integer>, 120 "length": <an integer>} 121 122with additional properties allowing access to the members: 123 124stride 125 The offset in bytes between the first data byte of each line in the data 126 array and the first byte of the next. 127 128offset 129 The offset in bytes from the start of the data array to the first byte of 130 the first line of the data in this component. 131 132width 133 The number of samples per line in this component 134 135height 136 The number of lines in this component 137 138length 139 The total length of the data for this component in bytes 140""" 141 def __init__(self, meta: VideoGrainComponentDict): 142 self.meta = meta 143 144 def __getitem__(self, key: Literal['stride', 'offset', 'width', 'height', 'length']) -> int: 145 return self.meta[key] 146 147 def __setitem__(self, key: Literal['stride', 'offset', 'width', 'height', 'length'], value: int) -> None: 148 self.meta[key] = value 149 150 def __iter__(self) -> Iterator[str]: 151 return self.meta.__iter__() 152 153 def __len__(self) -> int: 154 return self.meta.__len__() 155 156 def __eq__(self, other: object) -> bool: 157 return dict(self) == other 158 159 def __ne__(self, other: object) -> bool: 160 return not (self == other) 161 162 @property 163 def stride(self) -> int: 164 return self.meta['stride'] 165 166 @stride.setter 167 def stride(self, value: int) -> None: 168 self.meta['stride'] = value 169 170 @property 171 def offset(self) -> int: 172 return self.meta['offset'] 173 174 @offset.setter 175 def offset(self, value: int) -> None: 176 self.meta['offset'] = value 177 178 @property 179 def width(self) -> int: 180 return self.meta['width'] 181 182 @width.setter 183 def width(self, value: int) -> None: 184 self.meta['width'] = value 185 186 @property 187 def height(self) -> int: 188 return self.meta['height'] 189 190 @height.setter 191 def height(self, value: int) -> None: 192 self.meta['height'] = value 193 194 @property 195 def length(self) -> int: 196 return self.meta['length'] 197 198 @length.setter 199 def length(self, value: int) -> None: 200 self.meta['length'] = value
A class representing a video component, it may be treated as a dictionary of the form:
{"stride": <an integer>,
"offset": <an integer>,
"width": <an integer>,
"height": <an integer>,
"length": <an integer>}
with additional properties allowing access to the members:
stride The offset in bytes between the first data byte of each line in the data array and the first byte of the next.
offset The offset in bytes from the start of the data array to the first byte of the first line of the data in this component.
width The number of samples per line in this component
height The number of lines in this component
length The total length of the data for this component in bytes
Inherited Members
- collections.abc.Mapping
- get
- keys
- items
- values
202 class COMPONENT_LIST(MutableSequence): 203 def __init__(self, parent: "VideoGrain"): 204 self.parent = parent 205 206 @overload 207 def __getitem__(self, key: int) -> "VideoGrain.COMPONENT": ... 208 209 @overload # noqa: F811 210 def __getitem__(self, key: slice) -> "List[VideoGrain.COMPONENT]": ... 211 212 def __getitem__(self, key): # noqa: F811 213 if isinstance(key, int): 214 return type(self.parent).COMPONENT(self.parent.meta['grain']['cog_frame']['components'][key]) 215 else: 216 return [type(self.parent).COMPONENT( 217 self.parent.meta['grain']['cog_frame']['components'][k]) for k in range(len(self))[key]] 218 219 @overload 220 def __setitem__(self, key: int, value: VideoGrainComponentDict) -> None: ... 221 222 @overload # noqa: F811 223 def __setitem__(self, key: slice, value: Iterable[VideoGrainComponentDict]) -> None: ... 224 225 def __setitem__(self, key, value): # noqa: F811 226 if isinstance(key, int): 227 self.parent.meta['grain']['cog_frame']['components'][key] = type(self.parent).COMPONENT(value) 228 else: 229 values = iter(value) 230 for n in range(len(self))[key]: 231 self.parent.meta['grain']['cog_frame']['components'][n] = type(self.parent).COMPONENT(next(values)) 232 233 def __delitem__(self, key: Union[int, slice]) -> None: 234 del self.parent.meta['grain']['cog_frame']['components'][key] 235 236 def insert(self, key: int, value: VideoGrainComponentDict) -> None: 237 self.parent.meta['grain']['cog_frame']['components'].insert( 238 key, type(self.parent).COMPONENT(value)) # type: ignore 239 240 def __len__(self) -> int: 241 return len(self.parent.meta['grain']['cog_frame']['components']) 242 243 def __eq__(self, other: object) -> bool: 244 return list(self) == other 245 246 def __ne__(self, other: object) -> bool: 247 return not (self == other)
All the operations on a read-write sequence.
Concrete subclasses must provide __new__ or __init__, __getitem__, __setitem__, __delitem__, __len__, and insert().
236 def insert(self, key: int, value: VideoGrainComponentDict) -> None: 237 self.parent.meta['grain']['cog_frame']['components'].insert( 238 key, type(self.parent).COMPONENT(value)) # type: ignore
S.insert(index, value) -- insert value before index
Inherited Members
- collections.abc.MutableSequence
- append
- clear
- reverse
- extend
- pop
- remove
- collections.abc.Sequence
- index
- count