A python library for handling grain-based media in a python-native style. Please read the pydoc documentation for more details.
Provides constructor functions for various types of grains and classes that nicely wrap those grains, as well as a full serialisation and deserialisation library for GSF format.
To run the installer run
make install
to create a redistributable source tarball call
make source
To run the tests for Python 2 and 3 (with tox)
make test
The nmoscommon library used to provide the Timestamp class is needed during testing. This library is available here:
< git+https://github.com/jamesba/nmos-common/@python3 >
to install this package that one must be installed, to run the tests it must be available in a repository that pip will look at during package instalation (if you don't have such a repo you can set one up quickly using devpi).
As an example of using this in your own code the following is a simple program to load the contents of a GSF file and print the timestamp of each grain.
>>> from mediagrains.gsf import load
>>> f = open('examples/video.gsf', "rb")
>>> (head, segments) = load(f)
>>> print('\n'.join(str(grain.origin_timestamp) for grain in segments[1]))
1420102800:0
1420102800:20000000
1420102800:40000000
1420102800:60000000
1420102800:80000000
1420102800:100000000
1420102800:120000000
1420102800:140000000
1420102800:160000000
1420102800:180000000
Alternatively to create a new video grain in 10-bit planar YUV 4:2:0 and fill it with colour-bars:
>>> from mediagrains import VideoGrain
>>> from uuid import uuid1
>>> from mediagrains.cogenum import CogFrameFormat, CogFrameLayout
>>> src_id = uuid1()
>>> flow_id = uuid1()
>>> grain = VideoGrain(src_id, flow_id, cog_frame_format=CogFrameFormat.S16_422_10BIT, width=1920, height=1080)
>>> colours = [
... (0x3FF, 0x000, 0x3FF),
... (0x3FF, 0x3FF, 0x000),
... (0x3FF, 0x000, 0x000),
... (0x3FF, 0x3FF, 0x3FF),
... (0x3FF, 0x200, 0x3FF),
... (0x3FF, 0x3FF, 0x200) ]
>>> x_offset = [0, 0, 0]
>>> for colour in colours:
... i = 0
... for c in grain.components:
... for x in range(0,c.width//len(colours)):
... for y in range(0,c.height):
... grain.data[c.offset + y*c.stride + (x_offset[i] + x)*2 + 0] = colour[i] & 0xFF
... grain.data[c.offset + y*c.stride + (x_offset[i] + x)*2 + 1] = colour[i] >> 8
... x_offset[i] += c.width//len(colours)
... i += 1
The object grain can then be freely used for whatever video processing is desired, or it can be serialised into a GSF file as follows:
>>> from mediagrains.gsf import dump
>>> f = open('dummyfile.gsf', 'wb')
>>> dump(f, [grain])
>>> f.close()
The encoding module also supports a "progressive" mode where an encoder object is created and a dump started, then grains can be added and will be written to the output file as they are added.
>>> from uuid import uuid1
>>> from mediagrains import Grain
>>> from mediagrains.gsf import GSFEncoder
>>> src_id = uuid1()
>>> flow_id = uuid1()
>>> f = open('dummyfile.gsf', 'wb')
>>> enc = GSFEncoder(f)
>>> seg = enc.add_segment() # This must be done before the call to start_dump
>>> enc.start_dump() # This writes the file header and starts the export
>>> seg.add_grain(Grain(src_id, flow_id)) # Adds a grain and writes it to the file
>>> seg.add_grain(Grain(src_id, flow_id)) # Adds a grain and writes it to the file
>>> seg.add_grain(Grain(src_id, flow_id)) # Adds a grain and writes it to the file
>>> enc.end_dump() # This ends the export and finishes off the file
>>> f.close()
If the underlying file is seekable then the end_dump call will upade all segment metadata to list the correct grain count, otherwise the counts will be left at -1.