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
class VideoGrain(mediagrains.grains.Grain.Grain):
 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

VideoGrain( meta: Optional[mediagrains.typing.VideoGrainMetadataDict] = None, data: Union[SupportsBytes, bytes, numpy.ndarray, Awaitable[Union[SupportsBytes, bytes, numpy.ndarray, NoneType]], NoneType] = None, src_id: Optional[uuid.UUID] = None, flow_id: Optional[uuid.UUID] = None, origin_timestamp: Optional[mediatimestamp.immutable.timestamp.SupportsMediaTimestamp] = None, creation_timestamp: Optional[mediatimestamp.immutable.timestamp.SupportsMediaTimestamp] = None, sync_timestamp: Optional[mediatimestamp.immutable.timestamp.SupportsMediaTimestamp] = None, rate: fractions.Fraction = Fraction(25, 1), duration: fractions.Fraction = Fraction(1, 25), cog_frame_format: mediagrains.cogenums.CogFrameFormat = <CogFrameFormat.UNKNOWN: 4294967294>, width: int = 1920, height: int = 1080, cog_frame_layout: mediagrains.cogenums.CogFrameLayout = <CogFrameLayout.UNKNOWN: 4294967294>)
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)
meta: mediagrains.typing.VideoGrainMetadataDict
components
format: mediagrains.cogenums.CogFrameFormat
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'])
cog_frame_format: mediagrains.cogenums.CogFrameFormat
460    @property
461    def cog_frame_format(self) -> CogFrameFormat:
462        return CogFrameFormat(self.meta['grain']['cog_frame']['format'])
width: int
468    @property
469    def width(self) -> int:
470        return self.meta['grain']['cog_frame']['width']
height: int
476    @property
477    def height(self) -> int:
478        return self.meta['grain']['cog_frame']['height']
layout: mediagrains.cogenums.CogFrameLayout
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'])
cog_frame_layout: mediagrains.cogenums.CogFrameLayout
494    @property
495    def cog_frame_layout(self) -> CogFrameLayout:
496        return CogFrameLayout(self.meta['grain']['cog_frame']['layout'])
extension: int
502    @property
503    def extension(self) -> int:
504        return self.meta['grain']['cog_frame']['extension']
source_aspect_ratio: Optional[fractions.Fraction]
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
pixel_aspect_ratio: Optional[fractions.Fraction]
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
expected_length: int
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
media_rate: Optional[fractions.Fraction]
546    @property
547    def media_rate(self) -> Optional[Fraction]:
548        if self.rate:
549            return self.rate
550        else:
551            return None
class VideoGrain.COMPONENT(collections.abc.Mapping):
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

VideoGrain.COMPONENT(meta: mediagrains.typing.VideoGrainComponentDict)
141        def __init__(self, meta: VideoGrainComponentDict):
142            self.meta = meta
meta
stride: int
162        @property
163        def stride(self) -> int:
164            return self.meta['stride']
offset: int
170        @property
171        def offset(self) -> int:
172            return self.meta['offset']
width: int
178        @property
179        def width(self) -> int:
180            return self.meta['width']
height: int
186        @property
187        def height(self) -> int:
188            return self.meta['height']
length: int
194        @property
195        def length(self) -> int:
196            return self.meta['length']
Inherited Members
collections.abc.Mapping
get
keys
items
values
class VideoGrain.COMPONENT_LIST(collections.abc.MutableSequence):
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().

VideoGrain.COMPONENT_LIST(parent: VideoGrain)
203        def __init__(self, parent: "VideoGrain"):
204            self.parent = parent
parent
def insert( self, key: int, value: mediagrains.typing.VideoGrainComponentDict) -> None:
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